mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: java swing gui
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struct2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("application")
|
||||
}
|
||||
|
||||
group = "com.reajason.javaweb"
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":generator"))
|
||||
implementation(project(":packer"))
|
||||
implementation("com.formdev:flatlaf:3.7")
|
||||
implementation("com.miglayout:miglayout-swing:5.3")
|
||||
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("com.reajason.javaweb.desktop.memshell.MemShellDesktopApplication")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.reajason.javaweb.desktop.memshell;
|
||||
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.MemShellGeneratorFrame;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class MemShellDesktopApplication {
|
||||
public static void main(String[] args) {
|
||||
FlatLightLaf.setup();
|
||||
SwingUtilities.invokeLater(() -> new MemShellGeneratorFrame().setVisible(true));
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
package com.reajason.javaweb.desktop.memshell.controller;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerCategoryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
|
||||
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MemShellFormController {
|
||||
private final ConfigCatalogService configCatalogService;
|
||||
private final MemShellValidator validator;
|
||||
private final ConfigCatalogService.ConfigCatalog catalog;
|
||||
private final MemShellFormState state = new MemShellFormState();
|
||||
|
||||
public MemShellFormController(ConfigCatalogService configCatalogService, MemShellValidator validator) {
|
||||
this.configCatalogService = configCatalogService;
|
||||
this.validator = validator;
|
||||
this.catalog = configCatalogService.load();
|
||||
reconcileAfterServerChange(true);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public ConfigCatalogService getConfigCatalogService() { return configCatalogService; }
|
||||
public MemShellFormState getState() { return state; }
|
||||
public ConfigCatalogService.ConfigCatalog getCatalog() { return catalog; }
|
||||
public MemShellValidator getValidator() { return validator; }
|
||||
|
||||
public List<String> getServers() {
|
||||
return new ArrayList<>(catalog.getServers().keySet());
|
||||
}
|
||||
|
||||
public List<String> getServerVersionOptions() {
|
||||
return configCatalogService.getServerVersionOptions(state.getServer());
|
||||
}
|
||||
|
||||
public List<String> getShellTools() {
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null) return Collections.emptyList();
|
||||
LinkedHashSet<String> tools = new LinkedHashSet<>(toolMap.keySet());
|
||||
tools.add(ShellTool.Custom);
|
||||
return new ArrayList<>(tools);
|
||||
}
|
||||
|
||||
public List<String> getCustomShellTypes() {
|
||||
List<String> values = catalog.getServers().get(state.getServer());
|
||||
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
|
||||
}
|
||||
|
||||
public List<String> getShellTypesForCurrentTool() {
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null) return Collections.emptyList();
|
||||
if (ShellTool.Custom.equals(state.getShellTool())) {
|
||||
return getCustomShellTypes();
|
||||
}
|
||||
List<String> values = toolMap.get(state.getShellTool());
|
||||
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
|
||||
}
|
||||
|
||||
public List<PackerEntryModel> getFilteredPackers() {
|
||||
List<PackerEntryModel> out = new ArrayList<>();
|
||||
for (PackerCategoryModel c : catalog.getPackers()) {
|
||||
for (PackerEntryModel p : c.getPackers()) {
|
||||
if (p.isCategoryAnchor()) continue;
|
||||
String name = p.getName();
|
||||
String shellType = state.getShellType();
|
||||
String server = state.getServer();
|
||||
if (shellType != null && shellType.startsWith("Agent")) {
|
||||
if (name.startsWith("Agent")) out.add(p);
|
||||
continue;
|
||||
}
|
||||
if (server != null && server.startsWith("XXL")) {
|
||||
if (!name.startsWith("Agent")) out.add(p);
|
||||
continue;
|
||||
}
|
||||
if (!name.startsWith("Agent") && !name.toLowerCase(Locale.ROOT).startsWith("xxl")) {
|
||||
out.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public PackerEntryModel getSelectedPackerEntry() {
|
||||
String selected = state.getPackingMethod();
|
||||
if (selected == null || selected.trim().isEmpty()) return null;
|
||||
for (PackerCategoryModel c : catalog.getPackers()) {
|
||||
for (PackerEntryModel p : c.getPackers()) {
|
||||
if (selected.equals(p.getName())) return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<PackerSchemaFieldModel> getSelectedPackerFields() {
|
||||
PackerEntryModel p = getSelectedPackerEntry();
|
||||
return p == null ? Collections.<PackerSchemaFieldModel>emptyList() : p.getFields();
|
||||
}
|
||||
|
||||
public List<String> getCommandEncryptors() { return catalog.getCommandEncryptors(); }
|
||||
public List<String> getCommandImplementationClasses() { return catalog.getCommandImplementationClasses(); }
|
||||
|
||||
public void setServer(String server) {
|
||||
state.setServer(server);
|
||||
reconcileAfterServerChange(false);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setServerVersion(String version) { state.setServerVersion(version); }
|
||||
|
||||
public void setTargetJdkVersion(String value) {
|
||||
state.setTargetJdkVersion(value);
|
||||
try {
|
||||
state.setByPassJavaModule(Integer.parseInt(value) >= 53);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setShellTool(String tool) {
|
||||
handleShellToolChange(tool);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setShellType(String shellType) {
|
||||
state.setShellType(shellType);
|
||||
state.setUrlPattern("");
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setUrlPattern(String urlPattern) { state.setUrlPattern(urlPattern); }
|
||||
public void setDebug(boolean value) { state.setDebug(value); }
|
||||
public void setProbe(boolean value) { state.setProbe(value); }
|
||||
public void setByPassJavaModule(boolean value) { state.setByPassJavaModule(value); }
|
||||
public void setLambdaSuffix(boolean value) { state.setLambdaSuffix(value); }
|
||||
public void setShrink(boolean value) { state.setShrink(value); }
|
||||
public void setStaticInitialize(boolean value) { state.setStaticInitialize(value); }
|
||||
|
||||
public void setGodzillaPass(String v) { state.setGodzillaPass(v); }
|
||||
public void setGodzillaKey(String v) { state.setGodzillaKey(v); }
|
||||
public void setBehinderPass(String v) { state.setBehinderPass(v); }
|
||||
public void setAntSwordPass(String v) { state.setAntSwordPass(v); }
|
||||
public void setCommandParamName(String v) { state.setCommandParamName(v); }
|
||||
public void setCommandTemplate(String v) { state.setCommandTemplate(v); }
|
||||
public void setHeaderName(String v) { state.setHeaderName(v); }
|
||||
public void setHeaderValue(String v) { state.setHeaderValue(v); }
|
||||
public void setShellClassBase64(String v) { state.setShellClassBase64(v); }
|
||||
public void setEncryptor(String v) { state.setEncryptor(v); }
|
||||
public void setImplementationClass(String v) { state.setImplementationClass(v); }
|
||||
|
||||
public void setShellClassName(String v) {
|
||||
state.setShellClassName(v);
|
||||
autoDisableRandomIfManualNames();
|
||||
}
|
||||
|
||||
public void setInjectorClassName(String v) {
|
||||
state.setInjectorClassName(v);
|
||||
autoDisableRandomIfManualNames();
|
||||
}
|
||||
|
||||
public void setRandomClassName(boolean checked) {
|
||||
state.setRandomClassName(checked);
|
||||
if (checked) {
|
||||
state.setSavedShellClassName(state.getShellClassName());
|
||||
state.setSavedInjectorClassName(state.getInjectorClassName());
|
||||
state.setShellClassName("");
|
||||
state.setInjectorClassName("");
|
||||
} else {
|
||||
state.setShellClassName(state.getSavedShellClassName());
|
||||
state.setInjectorClassName(state.getSavedInjectorClassName());
|
||||
}
|
||||
}
|
||||
|
||||
public void setCustomInputMode(String mode) { state.setCustomInputMode(mode); }
|
||||
|
||||
public void setPacker(String packerName) {
|
||||
state.setPackingMethod(packerName);
|
||||
resetPackerCustomConfigToDefaults();
|
||||
}
|
||||
|
||||
public void setPackerCustomField(String key, Object value) {
|
||||
if (value == null) {
|
||||
state.getPackerCustomConfig().remove(key);
|
||||
} else {
|
||||
state.getPackerCustomConfig().put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackerCustomConfig() { return state.getPackerCustomConfig(); }
|
||||
|
||||
public boolean isUrlPatternVisible() {
|
||||
return validator.needsUrlPattern(state.getShellType());
|
||||
}
|
||||
|
||||
public boolean isCommandHeaderVisible() {
|
||||
return "BypassNginxWebSocket".equals(state.getShellType()) || "BypassNginxJakartaWebSocket".equals(state.getShellType());
|
||||
}
|
||||
|
||||
public boolean isProxyHeaderVisible() { return isCommandHeaderVisible(); }
|
||||
|
||||
public boolean isCommandParamVisible() {
|
||||
return state.getShellType() == null || !state.getShellType().contains("WebSocket");
|
||||
}
|
||||
|
||||
public MemShellValidator.Result validate() { return validator.validate(state); }
|
||||
|
||||
private void reconcileAfterServerChange(boolean initial) {
|
||||
List<String> serverVersions = getServerVersionOptions();
|
||||
if (!serverVersions.contains(state.getServerVersion())) {
|
||||
state.setServerVersion(serverVersions.get(0));
|
||||
}
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null || toolMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> toolKeys = new ArrayList<>(toolMap.keySet());
|
||||
String currentTool = state.getShellTool();
|
||||
String nextTool = toolMap.containsKey(currentTool) ? currentTool : toolKeys.get(0);
|
||||
state.setShellTool(nextTool);
|
||||
|
||||
String currentTargetJdk = state.getTargetJdkVersion();
|
||||
int currentJdk = parseInt(currentTargetJdk, 50);
|
||||
boolean raise = ("SpringWebFlux".equals(state.getServer()) || "XXLJOB".equals(state.getServer())) && currentJdk <= 52;
|
||||
state.setTargetJdkVersion(raise ? "52" : "50");
|
||||
state.setByPassJavaModule(parseInt(state.getTargetJdkVersion(), 50) >= 53);
|
||||
if (!initial) {
|
||||
state.setUrlPattern("");
|
||||
}
|
||||
|
||||
if (!serverVersions.contains(state.getServerVersion())) {
|
||||
state.setServerVersion(serverVersions.get(0));
|
||||
}
|
||||
ensureShellTypeValidForCurrentTool();
|
||||
}
|
||||
|
||||
private void ensureShellTypeValidForCurrentTool() {
|
||||
List<String> shellTypes = getShellTypesForCurrentTool();
|
||||
if (shellTypes.isEmpty()) {
|
||||
state.setShellType("");
|
||||
return;
|
||||
}
|
||||
if (!shellTypes.contains(state.getShellType())) {
|
||||
state.setShellType(shellTypes.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleShellToolChange(String value) {
|
||||
if (value == null || value.trim().isEmpty()) return;
|
||||
|
||||
state.setUrlPattern("");
|
||||
state.setShellClassName("");
|
||||
state.setInjectorClassName("");
|
||||
|
||||
if (ShellTool.Command.equals(value)) {
|
||||
state.setCommandParamName("");
|
||||
state.setImplementationClass("");
|
||||
state.setEncryptor("");
|
||||
} else if (ShellTool.Godzilla.equals(value)) {
|
||||
state.setGodzillaKey("");
|
||||
state.setGodzillaPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Behinder.equals(value)) {
|
||||
state.setBehinderPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Suo5.equals(value) || ShellTool.Suo5v2.equals(value)) {
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.AntSword.equals(value)) {
|
||||
state.setAntSwordPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.NeoreGeorg.equals(value)) {
|
||||
state.setHeaderName("Referer");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Custom.equals(value)) {
|
||||
state.setShellClassBase64("");
|
||||
} else if (ShellTool.Proxy.equals(value)) {
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
}
|
||||
|
||||
state.setShellTool(value);
|
||||
ensureShellTypeValidForCurrentTool();
|
||||
}
|
||||
|
||||
private void reconcilePackerSelection() {
|
||||
List<PackerEntryModel> filtered = getFilteredPackers();
|
||||
if (filtered.isEmpty()) {
|
||||
state.setPackingMethod("");
|
||||
state.getPackerCustomConfig().clear();
|
||||
return;
|
||||
}
|
||||
boolean exists = filtered.stream().anyMatch(p -> p.getName().equals(state.getPackingMethod()));
|
||||
if (!exists) {
|
||||
state.setPackingMethod(filtered.get(0).getName());
|
||||
resetPackerCustomConfigToDefaults();
|
||||
} else if (state.getPackerCustomConfig().isEmpty()) {
|
||||
resetPackerCustomConfigToDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetPackerCustomConfigToDefaults() {
|
||||
state.getPackerCustomConfig().clear();
|
||||
PackerEntryModel selected = getSelectedPackerEntry();
|
||||
if (selected != null) {
|
||||
state.getPackerCustomConfig().putAll(selected.getDefaultConfig());
|
||||
}
|
||||
}
|
||||
|
||||
private void autoDisableRandomIfManualNames() {
|
||||
if (state.isRandomClassName() && (!state.getShellClassName().trim().isEmpty() || !state.getInjectorClassName().trim().isEmpty())) {
|
||||
state.setRandomClassName(false);
|
||||
}
|
||||
if (!state.isRandomClassName()) {
|
||||
state.setSavedShellClassName(state.getShellClassName());
|
||||
state.setSavedInjectorClassName(state.getInjectorClassName());
|
||||
}
|
||||
}
|
||||
|
||||
private int parseInt(String v, int d) {
|
||||
try {
|
||||
return Integer.parseInt(v);
|
||||
} catch (Exception e) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
|
||||
public class DesktopMemShellGenerateResult {
|
||||
private final MemShellResult memShellResult;
|
||||
private final String packMethod;
|
||||
private final String packResult;
|
||||
private final boolean jarOutput;
|
||||
private final boolean agentOutput;
|
||||
|
||||
public DesktopMemShellGenerateResult(MemShellResult memShellResult, String packMethod, String packResult) {
|
||||
this.memShellResult = memShellResult;
|
||||
this.packMethod = packMethod;
|
||||
this.packResult = packResult;
|
||||
this.jarOutput = packMethod != null && packMethod.endsWith("Jar");
|
||||
this.agentOutput = packMethod != null && packMethod.startsWith("Agent");
|
||||
}
|
||||
|
||||
public MemShellResult getMemShellResult() { return memShellResult; }
|
||||
public String getPackMethod() { return packMethod; }
|
||||
public String getPackResult() { return packResult; }
|
||||
public boolean isJarOutput() { return jarOutput; }
|
||||
public boolean isAgentOutput() { return agentOutput; }
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MemShellFormState {
|
||||
private String server = "Tomcat";
|
||||
private String serverVersion = "Unknown";
|
||||
private String targetJdkVersion = "50";
|
||||
private boolean debug;
|
||||
private boolean byPassJavaModule;
|
||||
private boolean probe;
|
||||
private boolean lambdaSuffix;
|
||||
private boolean shrink = true;
|
||||
private boolean staticInitialize = true;
|
||||
|
||||
private String shellTool = "Godzilla";
|
||||
private String shellType = "Listener";
|
||||
private String urlPattern = "/*";
|
||||
|
||||
private String shellClassName = "";
|
||||
private String injectorClassName = "";
|
||||
|
||||
private String godzillaPass = "";
|
||||
private String godzillaKey = "";
|
||||
private String behinderPass = "";
|
||||
private String antSwordPass = "";
|
||||
private String commandParamName = "";
|
||||
private String commandTemplate = "";
|
||||
private String headerName = "User-Agent";
|
||||
private String headerValue = "";
|
||||
private String shellClassBase64 = "";
|
||||
private String encryptor = "";
|
||||
private String implementationClass = "";
|
||||
|
||||
private String packingMethod = "";
|
||||
private final Map<String, Object> packerCustomConfig = new LinkedHashMap<>();
|
||||
|
||||
private boolean randomClassName = true;
|
||||
private String customInputMode = "base64";
|
||||
|
||||
private String savedShellClassName = "";
|
||||
private String savedInjectorClassName = "";
|
||||
|
||||
public MemShellFormState copy() {
|
||||
MemShellFormState c = new MemShellFormState();
|
||||
c.server = server;
|
||||
c.serverVersion = serverVersion;
|
||||
c.targetJdkVersion = targetJdkVersion;
|
||||
c.debug = debug;
|
||||
c.byPassJavaModule = byPassJavaModule;
|
||||
c.probe = probe;
|
||||
c.lambdaSuffix = lambdaSuffix;
|
||||
c.shrink = shrink;
|
||||
c.staticInitialize = staticInitialize;
|
||||
c.shellTool = shellTool;
|
||||
c.shellType = shellType;
|
||||
c.urlPattern = urlPattern;
|
||||
c.shellClassName = shellClassName;
|
||||
c.injectorClassName = injectorClassName;
|
||||
c.godzillaPass = godzillaPass;
|
||||
c.godzillaKey = godzillaKey;
|
||||
c.behinderPass = behinderPass;
|
||||
c.antSwordPass = antSwordPass;
|
||||
c.commandParamName = commandParamName;
|
||||
c.commandTemplate = commandTemplate;
|
||||
c.headerName = headerName;
|
||||
c.headerValue = headerValue;
|
||||
c.shellClassBase64 = shellClassBase64;
|
||||
c.encryptor = encryptor;
|
||||
c.implementationClass = implementationClass;
|
||||
c.packingMethod = packingMethod;
|
||||
c.packerCustomConfig.putAll(packerCustomConfig);
|
||||
c.randomClassName = randomClassName;
|
||||
c.customInputMode = customInputMode;
|
||||
c.savedShellClassName = savedShellClassName;
|
||||
c.savedInjectorClassName = savedInjectorClassName;
|
||||
return c;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackerCustomConfig() { return packerCustomConfig; }
|
||||
|
||||
public String getServer() { return server; }
|
||||
public void setServer(String server) { this.server = server; }
|
||||
public String getServerVersion() { return serverVersion; }
|
||||
public void setServerVersion(String serverVersion) { this.serverVersion = serverVersion; }
|
||||
public String getTargetJdkVersion() { return targetJdkVersion; }
|
||||
public void setTargetJdkVersion(String targetJdkVersion) { this.targetJdkVersion = targetJdkVersion; }
|
||||
public boolean isDebug() { return debug; }
|
||||
public void setDebug(boolean debug) { this.debug = debug; }
|
||||
public boolean isByPassJavaModule() { return byPassJavaModule; }
|
||||
public void setByPassJavaModule(boolean byPassJavaModule) { this.byPassJavaModule = byPassJavaModule; }
|
||||
public boolean isProbe() { return probe; }
|
||||
public void setProbe(boolean probe) { this.probe = probe; }
|
||||
public boolean isLambdaSuffix() { return lambdaSuffix; }
|
||||
public void setLambdaSuffix(boolean lambdaSuffix) { this.lambdaSuffix = lambdaSuffix; }
|
||||
public boolean isShrink() { return shrink; }
|
||||
public void setShrink(boolean shrink) { this.shrink = shrink; }
|
||||
public boolean isStaticInitialize() { return staticInitialize; }
|
||||
public void setStaticInitialize(boolean staticInitialize) { this.staticInitialize = staticInitialize; }
|
||||
public String getShellTool() { return shellTool; }
|
||||
public void setShellTool(String shellTool) { this.shellTool = shellTool; }
|
||||
public String getShellType() { return shellType; }
|
||||
public void setShellType(String shellType) { this.shellType = shellType; }
|
||||
public String getUrlPattern() { return urlPattern; }
|
||||
public void setUrlPattern(String urlPattern) { this.urlPattern = urlPattern; }
|
||||
public String getShellClassName() { return shellClassName; }
|
||||
public void setShellClassName(String shellClassName) { this.shellClassName = shellClassName == null ? "" : shellClassName; }
|
||||
public String getInjectorClassName() { return injectorClassName; }
|
||||
public void setInjectorClassName(String injectorClassName) { this.injectorClassName = injectorClassName == null ? "" : injectorClassName; }
|
||||
public String getGodzillaPass() { return godzillaPass; }
|
||||
public void setGodzillaPass(String godzillaPass) { this.godzillaPass = nv(godzillaPass); }
|
||||
public String getGodzillaKey() { return godzillaKey; }
|
||||
public void setGodzillaKey(String godzillaKey) { this.godzillaKey = nv(godzillaKey); }
|
||||
public String getBehinderPass() { return behinderPass; }
|
||||
public void setBehinderPass(String behinderPass) { this.behinderPass = nv(behinderPass); }
|
||||
public String getAntSwordPass() { return antSwordPass; }
|
||||
public void setAntSwordPass(String antSwordPass) { this.antSwordPass = nv(antSwordPass); }
|
||||
public String getCommandParamName() { return commandParamName; }
|
||||
public void setCommandParamName(String commandParamName) { this.commandParamName = nv(commandParamName); }
|
||||
public String getCommandTemplate() { return commandTemplate; }
|
||||
public void setCommandTemplate(String commandTemplate) { this.commandTemplate = nv(commandTemplate); }
|
||||
public String getHeaderName() { return headerName; }
|
||||
public void setHeaderName(String headerName) { this.headerName = nv(headerName); }
|
||||
public String getHeaderValue() { return headerValue; }
|
||||
public void setHeaderValue(String headerValue) { this.headerValue = nv(headerValue); }
|
||||
public String getShellClassBase64() { return shellClassBase64; }
|
||||
public void setShellClassBase64(String shellClassBase64) { this.shellClassBase64 = nv(shellClassBase64); }
|
||||
public String getEncryptor() { return encryptor; }
|
||||
public void setEncryptor(String encryptor) { this.encryptor = nv(encryptor); }
|
||||
public String getImplementationClass() { return implementationClass; }
|
||||
public void setImplementationClass(String implementationClass) { this.implementationClass = nv(implementationClass); }
|
||||
public String getPackingMethod() { return packingMethod; }
|
||||
public void setPackingMethod(String packingMethod) { this.packingMethod = nv(packingMethod); }
|
||||
public boolean isRandomClassName() { return randomClassName; }
|
||||
public void setRandomClassName(boolean randomClassName) { this.randomClassName = randomClassName; }
|
||||
public String getCustomInputMode() { return customInputMode; }
|
||||
public void setCustomInputMode(String customInputMode) { this.customInputMode = nv(customInputMode); }
|
||||
public String getSavedShellClassName() { return savedShellClassName; }
|
||||
public void setSavedShellClassName(String savedShellClassName) { this.savedShellClassName = nv(savedShellClassName); }
|
||||
public String getSavedInjectorClassName() { return savedInjectorClassName; }
|
||||
public void setSavedInjectorClassName(String savedInjectorClassName) { this.savedInjectorClassName = nv(savedInjectorClassName); }
|
||||
|
||||
private static String nv(String v) { return v == null ? "" : v; }
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PackerCategoryModel {
|
||||
private String name;
|
||||
private final List<PackerEntryModel> packers = new ArrayList<>();
|
||||
|
||||
public PackerCategoryModel() {}
|
||||
public PackerCategoryModel(String name) { this.name = name; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public List<PackerEntryModel> getPackers() { return packers; }
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PackerEntryModel {
|
||||
private String categoryName;
|
||||
private String name;
|
||||
private String outputKind;
|
||||
private boolean categoryAnchor;
|
||||
private final List<PackerSchemaFieldModel> fields = new ArrayList<>();
|
||||
private final Map<String, Object> defaultConfig = new LinkedHashMap<>();
|
||||
|
||||
public String getCategoryName() { return categoryName; }
|
||||
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getOutputKind() { return outputKind; }
|
||||
public void setOutputKind(String outputKind) { this.outputKind = outputKind; }
|
||||
public boolean isCategoryAnchor() { return categoryAnchor; }
|
||||
public void setCategoryAnchor(boolean categoryAnchor) { this.categoryAnchor = categoryAnchor; }
|
||||
public List<PackerSchemaFieldModel> getFields() { return fields; }
|
||||
public Map<String, Object> getDefaultConfig() { return defaultConfig; }
|
||||
|
||||
public String displayLabel() {
|
||||
return categoryName == null || categoryName.equals(name) ? name : categoryName + " / " + name;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PackerSchemaFieldModel {
|
||||
public static class Option {
|
||||
private String value;
|
||||
private String label;
|
||||
|
||||
public Option() {}
|
||||
public Option(String value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
public String getValue() { return value; }
|
||||
public void setValue(String value) { this.value = value; }
|
||||
public String getLabel() { return label; }
|
||||
public void setLabel(String label) { this.label = label; }
|
||||
}
|
||||
|
||||
private String key;
|
||||
private String type;
|
||||
private boolean required;
|
||||
private Object defaultValue;
|
||||
private String description;
|
||||
private String descriptionI18nKey;
|
||||
private final List<Option> options = new ArrayList<>();
|
||||
|
||||
public String getKey() { return key; }
|
||||
public void setKey(String key) { this.key = key; }
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
public boolean isRequired() { return required; }
|
||||
public void setRequired(boolean required) { this.required = required; }
|
||||
public Object getDefaultValue() { return defaultValue; }
|
||||
public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getDescriptionI18nKey() { return descriptionI18nKey; }
|
||||
public void setDescriptionI18nKey(String descriptionI18nKey) { this.descriptionI18nKey = descriptionI18nKey; }
|
||||
public List<Option> getOptions() { return options; }
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package com.reajason.javaweb.desktop.memshell.service;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerCategoryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
|
||||
import com.reajason.javaweb.memshell.ServerFactory;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.spec.PackerFieldSchema;
|
||||
import com.reajason.javaweb.packer.spec.PackerOptionValue;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ConfigCatalogService {
|
||||
|
||||
public static class ConfigCatalog {
|
||||
private final Map<String, List<String>> servers;
|
||||
private final Map<String, Map<String, List<String>>> core;
|
||||
private final List<PackerCategoryModel> packers;
|
||||
private final List<String> commandEncryptors;
|
||||
private final List<String> commandImplementationClasses;
|
||||
|
||||
public ConfigCatalog(Map<String, List<String>> servers,
|
||||
Map<String, Map<String, List<String>>> core,
|
||||
List<PackerCategoryModel> packers,
|
||||
List<String> commandEncryptors,
|
||||
List<String> commandImplementationClasses) {
|
||||
this.servers = servers;
|
||||
this.core = core;
|
||||
this.packers = packers;
|
||||
this.commandEncryptors = commandEncryptors;
|
||||
this.commandImplementationClasses = commandImplementationClasses;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getServers() { return servers; }
|
||||
public Map<String, Map<String, List<String>>> getCore() { return core; }
|
||||
public List<PackerCategoryModel> getPackers() { return packers; }
|
||||
public List<String> getCommandEncryptors() { return commandEncryptors; }
|
||||
public List<String> getCommandImplementationClasses() { return commandImplementationClasses; }
|
||||
}
|
||||
|
||||
public ConfigCatalog load() {
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
Map<String, Map<String, List<String>>> core = new LinkedHashMap<>();
|
||||
|
||||
for (String serverName : ServerFactory.getSupportedServers()) {
|
||||
AbstractServer server = ServerFactory.getServer(serverName);
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> shellTypes = new ArrayList<>(server.getShellInjectorMapping().getSupportedShellTypes());
|
||||
servers.put(serverName, shellTypes);
|
||||
|
||||
Map<String, List<String>> toolMap = new LinkedHashMap<>();
|
||||
for (String tool : server.getSupportedShellTools()) {
|
||||
List<String> types = new ArrayList<>(server.getSupportedShellTypes(tool));
|
||||
if (!types.isEmpty()) {
|
||||
toolMap.put(tool, types);
|
||||
}
|
||||
}
|
||||
core.put(serverName, toolMap);
|
||||
}
|
||||
|
||||
List<PackerCategoryModel> packerModels = new ArrayList<>();
|
||||
for (Map.Entry<String, List<Packers>> entry : Packers.groupedPackers().entrySet()) {
|
||||
PackerCategoryModel category = new PackerCategoryModel(entry.getKey());
|
||||
for (Packers packerEnum : entry.getValue()) {
|
||||
PackerEntryModel p = new PackerEntryModel();
|
||||
p.setCategoryName(entry.getKey());
|
||||
p.setName(packerEnum.name());
|
||||
p.setOutputKind(packerEnum.getOutputKind());
|
||||
p.setCategoryAnchor(packerEnum.hasChildren());
|
||||
PackerSchema schema = packerEnum.getSchema();
|
||||
if (schema != null) {
|
||||
p.getDefaultConfig().putAll(schema.getDefaultConfig());
|
||||
for (PackerFieldSchema field : schema.getFields()) {
|
||||
PackerSchemaFieldModel f = new PackerSchemaFieldModel();
|
||||
f.setKey(field.getKey());
|
||||
f.setType(field.getType() == null ? null : field.getType().name());
|
||||
f.setRequired(field.isRequired());
|
||||
f.setDefaultValue(field.getDefaultValue());
|
||||
f.setDescription(field.getDescription());
|
||||
f.setDescriptionI18nKey(field.getDescriptionI18nKey());
|
||||
for (PackerOptionValue option : field.getOptions()) {
|
||||
f.getOptions().add(new PackerSchemaFieldModel.Option(option.getValue(), option.getLabel()));
|
||||
}
|
||||
p.getFields().add(f);
|
||||
}
|
||||
}
|
||||
category.getPackers().add(p);
|
||||
}
|
||||
packerModels.add(category);
|
||||
}
|
||||
|
||||
List<String> encryptors = Arrays.stream(CommandConfig.Encryptor.values()).map(Enum::name).collect(Collectors.toList());
|
||||
List<String> impls = Arrays.stream(CommandConfig.ImplementationClass.values()).map(Enum::name).collect(Collectors.toList());
|
||||
|
||||
return new ConfigCatalog(servers, core, packerModels, encryptors, impls);
|
||||
}
|
||||
|
||||
public List<String> getServerVersionOptions(String server) {
|
||||
if ("TongWeb".equals(server)) {
|
||||
return Arrays.asList("6", "7", "8");
|
||||
}
|
||||
if ("Jetty".equals(server)) {
|
||||
return Arrays.asList("6", "7+", "12");
|
||||
}
|
||||
return Collections.singletonList("Unknown");
|
||||
}
|
||||
|
||||
public List<String> getTargetJdkOptions() {
|
||||
return Arrays.asList("50", "52", "53", "55", "61", "65");
|
||||
}
|
||||
|
||||
public String getTargetJdkLabel(String value) {
|
||||
if ("50".equals(value)) return "Java6";
|
||||
if ("52".equals(value)) return "Java8";
|
||||
if ("53".equals(value)) return "Java9";
|
||||
if ("55".equals(value)) return "Java11";
|
||||
if ("61".equals(value)) return "Java17";
|
||||
if ("65".equals(value)) return "Java21";
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.reajason.javaweb.desktop.memshell.service;
|
||||
|
||||
import net.bytebuddy.jar.asm.ClassReader;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class CustomClassNameParser {
|
||||
public String parseClassNameFromBase64(String classBase64) {
|
||||
if (classBase64 == null || classBase64.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("class base64 is empty");
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(classBase64);
|
||||
return parseClassName(bytes);
|
||||
}
|
||||
|
||||
public String parseClassName(byte[] classBytes) {
|
||||
if (classBytes == null || classBytes.length == 0) {
|
||||
throw new IllegalArgumentException("class bytes are empty");
|
||||
}
|
||||
return new ClassReader(classBytes).getClassName().replace('/', '.');
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package com.reajason.javaweb.desktop.memshell.service;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GenerationService {
|
||||
|
||||
public DesktopMemShellGenerateResult generate(MemShellFormState s) {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(s.getServer())
|
||||
.serverVersion(s.getServerVersion())
|
||||
.shellTool(s.getShellTool())
|
||||
.shellType(s.getShellType())
|
||||
.targetJreVersion(parseInt(s.getTargetJdkVersion(), 50))
|
||||
.debug(s.isDebug())
|
||||
.byPassJavaModule(s.isByPassJavaModule())
|
||||
.probe(s.isProbe())
|
||||
.shrink(s.isShrink())
|
||||
.lambdaSuffix(s.isLambdaSuffix())
|
||||
.build();
|
||||
|
||||
InjectorConfig injectorConfig = InjectorConfig.builder()
|
||||
.urlPattern(blankToDefault(s.getUrlPattern(), "/*"))
|
||||
.injectorClassName(blankToNull(s.getInjectorClassName()))
|
||||
.staticInitialize(s.isStaticInitialize())
|
||||
.build();
|
||||
|
||||
ShellToolConfig shellToolConfig = buildShellToolConfig(s);
|
||||
MemShellResult memShellResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
|
||||
String packMethod = s.getPackingMethod();
|
||||
Packers packers = Packers.fromName(packMethod);
|
||||
Packer<?> packer = packers.getInstance();
|
||||
String packResult;
|
||||
if (packer instanceof JarPacker) {
|
||||
JarPacker jarPacker = (JarPacker) packer;
|
||||
packResult = Base64.getEncoder().encodeToString(jarPacker.packBytes(memShellResult.toJarPackerConfig()));
|
||||
} else {
|
||||
ClassPackerConfig<Object> classPackerConfig = cast(memShellResult.toClassPackerConfig());
|
||||
Map<String, Object> rawCustom = new LinkedHashMap<>(s.getPackerCustomConfig());
|
||||
classPackerConfig.setCustomConfig(((Packer<Object>) packer).resolveCustomConfig(rawCustom));
|
||||
packResult = ((Packer<Object>) packer).pack(classPackerConfig);
|
||||
}
|
||||
return new DesktopMemShellGenerateResult(memShellResult, packMethod, packResult);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static ClassPackerConfig<Object> cast(ClassPackerConfig<?> c) {
|
||||
return (ClassPackerConfig<Object>) c;
|
||||
}
|
||||
|
||||
private ShellToolConfig buildShellToolConfig(MemShellFormState s) {
|
||||
String tool = s.getShellTool();
|
||||
if (ShellTool.Godzilla.equals(tool)) {
|
||||
return GodzillaConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.pass(blankToNull(s.getGodzillaPass()))
|
||||
.key(blankToNull(s.getGodzillaKey()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.Behinder.equals(tool)) {
|
||||
return BehinderConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.pass(blankToNull(s.getBehinderPass()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.AntSword.equals(tool)) {
|
||||
return AntSwordConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.pass(blankToNull(s.getAntSwordPass()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.Suo5.equals(tool) || ShellTool.Suo5v2.equals(tool)) {
|
||||
return Suo5Config.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.NeoreGeorg.equals(tool)) {
|
||||
return NeoreGeorgConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.Proxy.equals(tool)) {
|
||||
return ProxyConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.Custom.equals(tool)) {
|
||||
return CustomConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.shellClassBase64(blankToNull(s.getShellClassBase64()))
|
||||
.build();
|
||||
}
|
||||
if (ShellTool.Command.equals(tool)) {
|
||||
return CommandConfig.builder()
|
||||
.shellClassName(blankToNull(s.getShellClassName()))
|
||||
.paramName(blankToNull(s.getCommandParamName()))
|
||||
.headerName(blankToNull(s.getHeaderName()))
|
||||
.headerValue(blankToNull(s.getHeaderValue()))
|
||||
.template(blankToNull(s.getCommandTemplate()))
|
||||
.encryptor(CommandConfig.Encryptor.fromString(blankToNull(s.getEncryptor())))
|
||||
.implementationClass(CommandConfig.ImplementationClass.fromString(blankToNull(s.getImplementationClass())))
|
||||
.build();
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported shell tool: " + tool);
|
||||
}
|
||||
|
||||
private int parseInt(String value, int defaultValue) {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (Exception ignored) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static String blankToNull(String s) {
|
||||
return s == null || s.trim().isEmpty() ? null : s;
|
||||
}
|
||||
|
||||
private static String blankToDefault(String s, String d) {
|
||||
return s == null || s.trim().isEmpty() ? d : s;
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
|
||||
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
|
||||
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
|
||||
import com.reajason.javaweb.desktop.memshell.service.GenerationService;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.panel.MainConfigPanel;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.panel.PackageConfigPanel;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.panel.ResultPanel;
|
||||
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MemShellGeneratorFrame extends JFrame {
|
||||
private final MemShellFormController controller;
|
||||
private final GenerationService generationService;
|
||||
private final MainConfigPanel mainConfigPanel;
|
||||
private final PackageConfigPanel packageConfigPanel;
|
||||
private final ResultPanel resultPanel;
|
||||
private final JButton generateButton = new JButton("生成内存马");
|
||||
private final JLabel statusLabel = new JLabel("就绪");
|
||||
private JComponent mainContentPanel;
|
||||
|
||||
public MemShellGeneratorFrame() {
|
||||
super("MemShellParty - MemShellGenerator");
|
||||
this.controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
this.generationService = new GenerationService();
|
||||
CustomClassNameParser customClassNameParser = new CustomClassNameParser();
|
||||
|
||||
this.resultPanel = new ResultPanel();
|
||||
this.mainConfigPanel = new MainConfigPanel(controller, customClassNameParser, this::refreshAll);
|
||||
this.packageConfigPanel = new PackageConfigPanel(controller, this::refreshAll);
|
||||
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
setMinimumSize(new Dimension(1180, 900));
|
||||
setSize(1280, 900);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
setLayout(new BorderLayout(6, 6));
|
||||
add(buildToolbar(), BorderLayout.NORTH);
|
||||
add(buildContent(), BorderLayout.CENTER);
|
||||
add(buildStatusBar(), BorderLayout.SOUTH);
|
||||
|
||||
generateButton.addActionListener(e -> onGenerate());
|
||||
resultPanel.clear();
|
||||
refreshAll();
|
||||
}
|
||||
|
||||
private JComponent buildToolbar() {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
p.setBorder(BorderFactory.createEmptyBorder(4, 8, 2, 8));
|
||||
return p;
|
||||
}
|
||||
|
||||
private JComponent buildContent() {
|
||||
JPanel topPane = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[][]"));
|
||||
JPanel leftColumn = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]6[]"));
|
||||
leftColumn.add(mainConfigPanel.getCorePanelComponent(), "growx");
|
||||
leftColumn.add(packageConfigPanel, "growx");
|
||||
|
||||
JPanel rightColumn = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]"));
|
||||
rightColumn.add(mainConfigPanel.getToolPanelComponent(), "growx");
|
||||
|
||||
JPanel topColumns = new JPanel(new MigLayout("insets 0, fillx, aligny top, gapx 8, wrap 2",
|
||||
"[grow,fill,sg topCol][grow,fill,sg topCol]",
|
||||
"[top]"));
|
||||
topColumns.add(leftColumn, "growx, pushx, top");
|
||||
topColumns.add(rightColumn, "growx, pushx, top");
|
||||
topPane.add(topColumns, "growx, pushy");
|
||||
|
||||
topPane.add(generateButton, "wrap, growx, gaptop 10");
|
||||
generateButton.setFont(generateButton.getFont().deriveFont(Font.BOLD, 14f));
|
||||
|
||||
JPanel content = new JPanel(new MigLayout("insets 0, fill, wrap 1", "[grow,fill]", "[][grow,fill]"));
|
||||
content.add(topPane, "growx");
|
||||
content.add(resultPanel, "grow, push");
|
||||
mainContentPanel = content;
|
||||
return content;
|
||||
}
|
||||
|
||||
private JComponent buildStatusBar() {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
p.setBorder(BorderFactory.createEmptyBorder(3, 8, 3, 8));
|
||||
p.add(statusLabel, BorderLayout.WEST);
|
||||
return p;
|
||||
}
|
||||
|
||||
private void onGenerate() {
|
||||
com.reajason.javaweb.desktop.memshell.validation.MemShellValidator.Result validation = controller.validate();
|
||||
if (!validation.isValid()) {
|
||||
statusLabel.setText("校验失败");
|
||||
SwingUiUtil.showError(this, validation.firstMessage());
|
||||
return;
|
||||
}
|
||||
generateButton.setEnabled(false);
|
||||
statusLabel.setText("生成中...");
|
||||
|
||||
SwingWorker<DesktopMemShellGenerateResult, Void> worker = new SwingWorker<DesktopMemShellGenerateResult, Void>() {
|
||||
@Override
|
||||
protected DesktopMemShellGenerateResult doInBackground() {
|
||||
return generationService.generate(controller.getState().copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
generateButton.setEnabled(true);
|
||||
try {
|
||||
DesktopMemShellGenerateResult result = get();
|
||||
resultPanel.showResult(result);
|
||||
statusLabel.setText("生成成功");
|
||||
} catch (Exception ex) {
|
||||
statusLabel.setText("生成失败");
|
||||
SwingUiUtil.showError(MemShellGeneratorFrame.this, "生成失败: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage()));
|
||||
}
|
||||
}
|
||||
};
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
public void refreshAll() {
|
||||
mainConfigPanel.refreshFromController();
|
||||
packageConfigPanel.refreshFromController();
|
||||
repaint();
|
||||
revalidate();
|
||||
}
|
||||
|
||||
JComponent getMainContentPanel() {
|
||||
return mainContentPanel;
|
||||
}
|
||||
|
||||
JButton getGenerateButton() {
|
||||
return generateButton;
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.panel.tool.*;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MainConfigPanel extends JPanel {
|
||||
private final MemShellFormController controller;
|
||||
private final Runnable refreshAll;
|
||||
private boolean updating;
|
||||
|
||||
private final JComboBox<String> serverCombo = new JComboBox<>();
|
||||
private final JComboBox<String> serverVersionCombo = new JComboBox<>();
|
||||
private final JComboBox<String> shellToolCombo = new JComboBox<>();
|
||||
private final JComboBox<String> targetJdkCombo = new JComboBox<>();
|
||||
|
||||
private final JCheckBox debugCheck = new JCheckBox("调试模式");
|
||||
private final JCheckBox probeCheck = new JCheckBox("回显模式");
|
||||
private final JCheckBox bypassCheck = new JCheckBox("绕过模块限制");
|
||||
private final JCheckBox lambdaCheck = new JCheckBox("Lambda 类名后缀");
|
||||
private final JCheckBox shrinkCheck = new JCheckBox("缩小字节码");
|
||||
private final JCheckBox staticInitCheck = new JCheckBox("静态初始化");
|
||||
|
||||
private final JPanel corePanel;
|
||||
private final JPanel toolPanelWrap;
|
||||
private final JPanel toolCardPanel = new JPanel(new CardLayout());
|
||||
private final Map<String, RefreshableToolPanel> toolPanels = new LinkedHashMap<>();
|
||||
|
||||
public MainConfigPanel(MemShellFormController controller, CustomClassNameParser parser, Runnable refreshAll) {
|
||||
super(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]6[]"));
|
||||
this.controller = controller;
|
||||
this.refreshAll = refreshAll;
|
||||
|
||||
corePanel = new JPanel(new MigLayout("insets 8, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]4[]"));
|
||||
corePanel.setBorder(BorderFactory.createTitledBorder("核心配置"));
|
||||
corePanel.add(labeled("服务类型", serverCombo), "growx");
|
||||
corePanel.add(labeled("服务版本", serverVersionCombo), "growx");
|
||||
corePanel.add(labeled("内存马工具", shellToolCombo), "growx");
|
||||
corePanel.add(labeled("JRE 版本", targetJdkCombo), "growx");
|
||||
|
||||
JPanel togglePanel = new JPanel(new MigLayout("insets 0, gapx 8, gapy 2, wrap 3", "[grow,fill][grow,fill][grow,fill]", "[]"));
|
||||
togglePanel.add(debugCheck);
|
||||
togglePanel.add(probeCheck);
|
||||
togglePanel.add(bypassCheck);
|
||||
togglePanel.add(lambdaCheck);
|
||||
togglePanel.add(shrinkCheck);
|
||||
togglePanel.add(staticInitCheck);
|
||||
corePanel.add(togglePanel, "span 2, growx");
|
||||
add(corePanel, "growx");
|
||||
|
||||
toolPanelWrap = new JPanel(new BorderLayout());
|
||||
toolPanelWrap.setBorder(BorderFactory.createTitledBorder("内存马功能"));
|
||||
// Keep the active tool panel at preferred height to avoid large blank sections.
|
||||
toolPanelWrap.add(toolCardPanel, BorderLayout.NORTH);
|
||||
add(toolPanelWrap, "growx");
|
||||
|
||||
registerToolPanel("Godzilla", new GodzillaToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Command", new CommandToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Behinder", new BehinderToolPanel(controller, refreshAll));
|
||||
registerToolPanel("AntSword", new AntSwordToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Suo5", new Suo5ToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Suo5v2", new Suo5ToolPanel(controller, refreshAll));
|
||||
registerToolPanel("NeoreGeorg", new NeoRegToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Proxy", new ProxyToolPanel(controller, refreshAll));
|
||||
registerToolPanel("Custom", new CustomToolPanel(controller, parser, refreshAll));
|
||||
|
||||
bindEvents();
|
||||
}
|
||||
|
||||
private void registerToolPanel(String key, RefreshableToolPanel panel) {
|
||||
toolPanels.put(key, panel);
|
||||
toolCardPanel.add((Component) panel, key);
|
||||
}
|
||||
|
||||
private JPanel labeled(String label, JComponent component) {
|
||||
JPanel p = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
|
||||
p.add(new JLabel(label));
|
||||
p.add(component, "growx");
|
||||
return p;
|
||||
}
|
||||
|
||||
private void bindEvents() {
|
||||
serverCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = serverCombo.getSelectedItem();
|
||||
if (item != null) {
|
||||
controller.setServer(String.valueOf(item));
|
||||
refreshAll.run();
|
||||
}
|
||||
});
|
||||
serverVersionCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = serverVersionCombo.getSelectedItem();
|
||||
if (item != null) controller.setServerVersion(String.valueOf(item));
|
||||
});
|
||||
shellToolCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = shellToolCombo.getSelectedItem();
|
||||
if (item != null) {
|
||||
controller.setShellTool(String.valueOf(item));
|
||||
refreshAll.run();
|
||||
}
|
||||
});
|
||||
targetJdkCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = targetJdkCombo.getSelectedItem();
|
||||
if (item != null) {
|
||||
controller.setTargetJdkVersion(String.valueOf(item));
|
||||
refreshAll.run();
|
||||
}
|
||||
});
|
||||
|
||||
debugCheck.addActionListener(e -> controller.setDebug(debugCheck.isSelected()));
|
||||
probeCheck.addActionListener(e -> controller.setProbe(probeCheck.isSelected()));
|
||||
bypassCheck.addActionListener(e -> controller.setByPassJavaModule(bypassCheck.isSelected()));
|
||||
lambdaCheck.addActionListener(e -> controller.setLambdaSuffix(lambdaCheck.isSelected()));
|
||||
shrinkCheck.addActionListener(e -> controller.setShrink(shrinkCheck.isSelected()));
|
||||
staticInitCheck.addActionListener(e -> controller.setStaticInitialize(staticInitCheck.isSelected()));
|
||||
}
|
||||
|
||||
public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
updating = true;
|
||||
try {
|
||||
setComboItems(serverCombo, controller.getServers(), s.getServer());
|
||||
setComboItems(serverVersionCombo, controller.getServerVersionOptions(), s.getServerVersion());
|
||||
setComboItems(shellToolCombo, controller.getShellTools(), s.getShellTool());
|
||||
setComboItems(targetJdkCombo, controller.getConfigCatalogService().getTargetJdkOptions(), s.getTargetJdkVersion());
|
||||
|
||||
debugCheck.setSelected(s.isDebug());
|
||||
probeCheck.setSelected(s.isProbe());
|
||||
bypassCheck.setSelected(s.isByPassJavaModule());
|
||||
lambdaCheck.setSelected(s.isLambdaSuffix());
|
||||
shrinkCheck.setSelected(s.isShrink());
|
||||
staticInitCheck.setSelected(s.isStaticInitialize());
|
||||
} finally {
|
||||
updating = false;
|
||||
}
|
||||
|
||||
CardLayout cardLayout = (CardLayout) toolCardPanel.getLayout();
|
||||
cardLayout.show(toolCardPanel, s.getShellTool());
|
||||
RefreshableToolPanel toolPanel = toolPanels.get(s.getShellTool());
|
||||
if (toolPanel != null) {
|
||||
toolPanel.refreshFromController();
|
||||
}
|
||||
}
|
||||
|
||||
private void setComboItems(JComboBox<String> combo, List<String> items, String selected) {
|
||||
combo.removeAllItems();
|
||||
for (String item : items) combo.addItem(item);
|
||||
if (selected != null) combo.setSelectedItem(selected);
|
||||
}
|
||||
|
||||
public JComponent getCorePanelComponent() {
|
||||
return corePanel;
|
||||
}
|
||||
|
||||
public JComponent getToolPanelComponent() {
|
||||
return toolPanelWrap;
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PackageConfigPanel extends JPanel {
|
||||
private final MemShellFormController controller;
|
||||
private final Runnable refreshAll;
|
||||
private boolean updating;
|
||||
|
||||
private final JComboBox<PackerEntryModel> packerCombo = new JComboBox<>();
|
||||
private final JPanel dynamicFieldsPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
|
||||
|
||||
public PackageConfigPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(new MigLayout("insets 8, fillx, wrap 1", "[grow,fill]", "[]4[]"));
|
||||
this.controller = controller;
|
||||
this.refreshAll = refreshAll;
|
||||
setBorder(BorderFactory.createTitledBorder("打包配置"));
|
||||
|
||||
JPanel top = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
|
||||
top.add(new JLabel("打包方式"));
|
||||
top.add(packerCombo, "growx");
|
||||
add(top, "growx");
|
||||
add(dynamicFieldsPanel, "growx");
|
||||
|
||||
packerCombo.setRenderer(new DefaultListCellRenderer() {
|
||||
@Override
|
||||
public java.awt.Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
if (value instanceof PackerEntryModel) {
|
||||
PackerEntryModel p = (PackerEntryModel) value;
|
||||
setText(p.displayLabel());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
packerCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = packerCombo.getSelectedItem();
|
||||
if (item instanceof PackerEntryModel) {
|
||||
PackerEntryModel p = (PackerEntryModel) item;
|
||||
controller.setPacker(p.getName());
|
||||
refreshAll.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void refreshFromController() {
|
||||
updating = true;
|
||||
try {
|
||||
DefaultComboBoxModel<PackerEntryModel> model = new DefaultComboBoxModel<>();
|
||||
List<PackerEntryModel> filtered = controller.getFilteredPackers();
|
||||
PackerEntryModel selected = controller.getSelectedPackerEntry();
|
||||
for (PackerEntryModel p : filtered) model.addElement(p);
|
||||
packerCombo.setModel(model);
|
||||
if (selected != null) packerCombo.setSelectedItem(selected);
|
||||
rebuildDynamicFields(controller.getSelectedPackerFields(), controller.getPackerCustomConfig());
|
||||
} finally {
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuildDynamicFields(List<PackerSchemaFieldModel> fields, Map<String, Object> currentValues) {
|
||||
dynamicFieldsPanel.removeAll();
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
dynamicFieldsPanel.revalidate();
|
||||
dynamicFieldsPanel.repaint();
|
||||
return;
|
||||
}
|
||||
for (PackerSchemaFieldModel field : fields) {
|
||||
String type = field.getType();
|
||||
if (!"BOOLEAN".equals(type) && !"STRING".equals(type) && !"ENUM".equals(type) && !"INTEGER".equals(type)) {
|
||||
continue;
|
||||
}
|
||||
Object value = currentValues.get(field.getKey());
|
||||
if ("BOOLEAN".equals(type)) {
|
||||
JCheckBox check = new JCheckBox(field.getKey());
|
||||
check.setSelected(Boolean.TRUE.equals(value));
|
||||
check.addActionListener(e -> controller.setPackerCustomField(field.getKey(), check.isSelected()));
|
||||
dynamicFieldsPanel.add(check, "span 2, growx");
|
||||
continue;
|
||||
}
|
||||
dynamicFieldsPanel.add(new JLabel(field.getKey()));
|
||||
if ("ENUM".equals(type)) {
|
||||
JComboBox<String> combo = new JComboBox<>();
|
||||
for (PackerSchemaFieldModel.Option option : field.getOptions()) {
|
||||
combo.addItem(option.getValue());
|
||||
}
|
||||
if (value != null) combo.setSelectedItem(String.valueOf(value));
|
||||
combo.addActionListener(e -> controller.setPackerCustomField(field.getKey(), combo.getSelectedItem()));
|
||||
dynamicFieldsPanel.add(combo, "growx");
|
||||
} else {
|
||||
JTextField text = new JTextField(value == null ? "" : String.valueOf(value));
|
||||
text.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override public void insertUpdate(DocumentEvent e) { changed(); }
|
||||
@Override public void removeUpdate(DocumentEvent e) { changed(); }
|
||||
@Override public void changedUpdate(DocumentEvent e) { changed(); }
|
||||
private void changed() {
|
||||
if (updating) return;
|
||||
if ("INTEGER".equals(type)) {
|
||||
String raw = text.getText().trim();
|
||||
if (raw.isEmpty()) {
|
||||
controller.setPackerCustomField(field.getKey(), null);
|
||||
} else {
|
||||
try {
|
||||
controller.setPackerCustomField(field.getKey(), Integer.parseInt(raw));
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
controller.setPackerCustomField(field.getKey(), text.getText());
|
||||
}
|
||||
}
|
||||
});
|
||||
dynamicFieldsPanel.add(text, "growx");
|
||||
}
|
||||
}
|
||||
dynamicFieldsPanel.revalidate();
|
||||
dynamicFieldsPanel.repaint();
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
|
||||
import com.reajason.javaweb.desktop.memshell.util.ClipboardUtil;
|
||||
import com.reajason.javaweb.desktop.memshell.util.FileSaveUtil;
|
||||
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ResultPanel extends JPanel {
|
||||
private final JTabbedPane tabs = new JTabbedPane();
|
||||
private final BasicInfoView basicInfoView = new BasicInfoView();
|
||||
private final JTextArea packResultArea = createTextArea();
|
||||
private final JTextArea shellArea = createTextArea();
|
||||
private final JTextArea injectorArea = createTextArea();
|
||||
private final JLabel packHeaderLabel = new JLabel("未生成");
|
||||
private DesktopMemShellGenerateResult current;
|
||||
|
||||
public ResultPanel() {
|
||||
super(new BorderLayout());
|
||||
|
||||
JPanel packTab = new JPanel(new MigLayout("insets 6, fill, wrap 1", "[grow,fill]", "[][grow]"));
|
||||
packTab.add(wrapBasicInfoPanel(), "growx");
|
||||
packTab.add(wrapPackResultPanel(), "grow, push");
|
||||
|
||||
tabs.addTab("生成结果", packTab);
|
||||
tabs.addTab("内存马", wrapBase64Panel("内存马类字节(Base64)", shellArea, true, true));
|
||||
tabs.addTab("注入器", wrapBase64Panel("注入器类字节(Base64)", injectorArea, false, true));
|
||||
add(tabs, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private JPanel wrapBasicInfoPanel() {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
|
||||
top.add(new JLabel("基本信息"));
|
||||
|
||||
Border cardBorder = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(UIManager.getColor("Component.borderColor") == null ? Color.LIGHT_GRAY : UIManager.getColor("Component.borderColor")),
|
||||
BorderFactory.createEmptyBorder(6, 6, 6, 6)
|
||||
);
|
||||
p.setBorder(cardBorder);
|
||||
p.add(top, BorderLayout.NORTH);
|
||||
p.add(basicInfoView, BorderLayout.CENTER);
|
||||
return p;
|
||||
}
|
||||
|
||||
private JPanel wrapPackResultPanel() {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
|
||||
JButton copyBtn = new JButton("复制");
|
||||
JButton saveBtn = new JButton("保存");
|
||||
copyBtn.addActionListener(e -> ClipboardUtil.copyText(packResultArea.getText()));
|
||||
saveBtn.addActionListener(e -> savePackResult());
|
||||
top.add(new JLabel("打包结果"));
|
||||
top.add(packHeaderLabel);
|
||||
top.add(copyBtn);
|
||||
top.add(saveBtn);
|
||||
p.add(top, BorderLayout.NORTH);
|
||||
p.add(new JScrollPane(packResultArea), BorderLayout.CENTER);
|
||||
return p;
|
||||
}
|
||||
|
||||
private JPanel wrapBase64Panel(String title, JTextArea area, boolean shell, boolean saveClass) {
|
||||
JPanel p = new JPanel(new BorderLayout());
|
||||
JPanel top = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 3));
|
||||
JButton copyBtn = new JButton("复制");
|
||||
JButton saveBtn = new JButton(saveClass ? "保存 .class" : "保存");
|
||||
copyBtn.addActionListener(e -> ClipboardUtil.copyText(area.getText()));
|
||||
saveBtn.addActionListener(e -> {
|
||||
if (current == null) return;
|
||||
try {
|
||||
MemShellResult r = current.getMemShellResult();
|
||||
if (shell) {
|
||||
FileSaveUtil.saveBase64AsBytes(this, simpleClassFileName(r.getShellClassName()), r.getShellBytesBase64Str(), "class");
|
||||
} else {
|
||||
FileSaveUtil.saveBase64AsBytes(this, simpleClassFileName(r.getInjectorClassName()), r.getInjectorBytesBase64Str(), "class");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
SwingUiUtil.showError(this, "保存失败: " + ex.getMessage());
|
||||
}
|
||||
});
|
||||
top.add(new JLabel(title));
|
||||
top.add(copyBtn);
|
||||
top.add(saveBtn);
|
||||
p.add(top, BorderLayout.NORTH);
|
||||
p.add(new JScrollPane(area), BorderLayout.CENTER);
|
||||
return p;
|
||||
}
|
||||
|
||||
private JTextArea createTextArea() {
|
||||
JTextArea area = new JTextArea();
|
||||
area.setEditable(false);
|
||||
area.setLineWrap(true);
|
||||
area.setWrapStyleWord(true);
|
||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
return area;
|
||||
}
|
||||
|
||||
public void showResult(DesktopMemShellGenerateResult result) {
|
||||
this.current = result;
|
||||
MemShellResult r = result.getMemShellResult();
|
||||
basicInfoView.setResult(result);
|
||||
packHeaderLabel.setText(result.getPackMethod() + (result.getPackResult() == null ? "" : " (" + result.getPackResult().length() + ")"));
|
||||
if (result.isJarOutput() || result.isAgentOutput()) {
|
||||
packResultArea.setText(buildUsageText(result));
|
||||
} else {
|
||||
packResultArea.setText(result.getPackResult());
|
||||
}
|
||||
shellArea.setText(r.getShellBytesBase64Str());
|
||||
injectorArea.setText(r.getInjectorBytesBase64Str());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
current = null;
|
||||
basicInfoView.clear();
|
||||
packResultArea.setText("");
|
||||
shellArea.setText("");
|
||||
injectorArea.setText("");
|
||||
packHeaderLabel.setText("未生成");
|
||||
}
|
||||
|
||||
private String buildUsageText(DesktopMemShellGenerateResult result) {
|
||||
if (result.isAgentOutput()) {
|
||||
return "1. 点击保存导出 Agent Jar\n2. 上传到目标机器\n3. 使用 jattach/attach 方式加载\n4. 按生成配置尝试连接/触发\n\n(下方“保存”按钮会保存打包后的 Jar)";
|
||||
}
|
||||
return "1. 点击保存导出 Jar\n2. 按目标环境触发类加载\n3. 使用基本信息中的参数连接内存马\n\n(下方“保存”按钮会保存打包后的 Jar)";
|
||||
}
|
||||
|
||||
private void savePackResult() {
|
||||
if (current == null) return;
|
||||
try {
|
||||
if (current.isJarOutput() || current.isAgentOutput()) {
|
||||
String baseName = current.getMemShellResult().getShellConfig().getServer() + current.getMemShellResult().getShellConfig().getShellTool() + (current.isAgentOutput() ? "MemShellAgent" : "MemShell");
|
||||
FileSaveUtil.saveBase64AsBytes(this, baseName + ".jar", current.getPackResult(), "jar");
|
||||
} else {
|
||||
FileSaveUtil.saveText(this, current.getPackMethod() + ".txt", current.getPackResult());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
SwingUiUtil.showError(this, "保存失败: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static String simpleClassFileName(String className) {
|
||||
if (className == null || className.trim().isEmpty()) return "output.class";
|
||||
int idx = className.lastIndexOf('.');
|
||||
return (idx >= 0 ? className.substring(idx + 1) : className) + ".class";
|
||||
}
|
||||
|
||||
public JComponent getBasicInfoComponent() {
|
||||
return basicInfoView;
|
||||
}
|
||||
|
||||
static final class BasicInfoView extends JPanel {
|
||||
private final JPanel content = new JPanel(new MigLayout("insets 0, fillx, gapx 10, gapy 2, wrap 2", "[right]10[grow,fill]", "[]"));
|
||||
|
||||
BasicInfoView() {
|
||||
super(new BorderLayout());
|
||||
add(content, BorderLayout.CENTER);
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
content.removeAll();
|
||||
content.revalidate();
|
||||
content.repaint();
|
||||
}
|
||||
|
||||
void setResult(DesktopMemShellGenerateResult result) {
|
||||
content.removeAll();
|
||||
MemShellResult r = result.getMemShellResult();
|
||||
ShellConfig shellConfig = r.getShellConfig();
|
||||
ShellToolConfig toolConfig = r.getShellToolConfig();
|
||||
appendToolRows(shellConfig, toolConfig);
|
||||
row("注入器类名", r.getInjectorClassName() + " (" + r.getInjectorSize() + " bytes)");
|
||||
row("内存马类名", r.getShellClassName() + " (" + r.getShellSize() + " bytes)");
|
||||
content.revalidate();
|
||||
content.repaint();
|
||||
}
|
||||
|
||||
private void appendToolRows(ShellConfig shellConfig, ShellToolConfig toolConfig) {
|
||||
if (toolConfig == null) {
|
||||
row("参数", "无");
|
||||
return;
|
||||
}
|
||||
String tool = shellConfig == null ? null : shellConfig.getShellTool();
|
||||
if (toolConfig instanceof GodzillaConfig) {
|
||||
GodzillaConfig c = (GodzillaConfig) toolConfig;
|
||||
row("密码", c.getPass());
|
||||
row("密钥", c.getKey());
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof BehinderConfig) {
|
||||
BehinderConfig c = (BehinderConfig) toolConfig;
|
||||
row("密码", c.getPass());
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof CommandConfig) {
|
||||
CommandConfig c = (CommandConfig) toolConfig;
|
||||
row("参数名", c.getParamName());
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
row("加密器", c.getEncryptor() == null ? "" : c.getEncryptor().name());
|
||||
row("实现类", c.getImplementationClass() == null ? "" : c.getImplementationClass().name());
|
||||
if (c.getTemplate() != null) {
|
||||
row("命令模板", c.getTemplate());
|
||||
}
|
||||
} else if (toolConfig instanceof AntSwordConfig) {
|
||||
AntSwordConfig c = (AntSwordConfig) toolConfig;
|
||||
row("密码", c.getPass());
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof Suo5Config) {
|
||||
Suo5Config c = (Suo5Config) toolConfig;
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof ProxyConfig) {
|
||||
ProxyConfig c = (ProxyConfig) toolConfig;
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof NeoreGeorgConfig) {
|
||||
NeoreGeorgConfig c = (NeoreGeorgConfig) toolConfig;
|
||||
row("请求头", c.getHeaderName() + ": " + c.getHeaderValue());
|
||||
} else if (toolConfig instanceof CustomConfig) {
|
||||
CustomConfig c = (CustomConfig) toolConfig;
|
||||
String v = c.getShellClassBase64();
|
||||
row("自定义类(Base64)", v == null ? "" : (v.length() > 32 ? v.substring(0, 32) + "..." : v));
|
||||
} else if (tool != null) {
|
||||
row("工具类型", tool);
|
||||
}
|
||||
}
|
||||
|
||||
private void row(String key, String value) {
|
||||
addValueRow(key, value);
|
||||
}
|
||||
|
||||
private void addValueRow(String key, String value) {
|
||||
String text = value == null ? "" : value;
|
||||
content.add(new JLabel(key));
|
||||
JTextField tf = new JTextField(text);
|
||||
tf.setEditable(false);
|
||||
tf.setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4));
|
||||
tf.setOpaque(true);
|
||||
tf.setBackground(UIManager.getColor("Panel.background"));
|
||||
tf.setToolTipText(value == null ? "" : value);
|
||||
tf.setToolTipText(text);
|
||||
content.add(tf, "growx, wrap");
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class AbstractToolPanel extends JPanel implements RefreshableToolPanel {
|
||||
protected final MemShellFormController controller;
|
||||
protected final Runnable refreshAll;
|
||||
protected boolean updating;
|
||||
|
||||
protected final JComboBox<String> shellTypeCombo = new JComboBox<>();
|
||||
protected final JTextField urlPatternField = new JTextField();
|
||||
protected final JPanel shellTypeAndUrlRow = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[50%,fill][50%,fill]", "[]"));
|
||||
protected JPanel urlPatternRow = new JPanel(new BorderLayout());
|
||||
protected final JCheckBox randomClassNameCheck = new JCheckBox("随机类名");
|
||||
protected final JPanel manualClassPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
|
||||
protected final JTextField shellClassNameField = new JTextField();
|
||||
protected final JTextField injectorClassNameField = new JTextField();
|
||||
|
||||
protected AbstractToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
this.controller = controller;
|
||||
this.refreshAll = refreshAll;
|
||||
setLayout(new MigLayout("insets 6, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]4[]"));
|
||||
buildCommonShellTypeSection();
|
||||
}
|
||||
|
||||
protected void buildCommonShellTypeSection() {
|
||||
shellTypeAndUrlRow.add(labeled("内存马挂载类型", shellTypeCombo), "growx");
|
||||
urlPatternRow = labeled("请求路径", urlPatternField);
|
||||
shellTypeAndUrlRow.add(urlPatternRow, "growx");
|
||||
add(shellTypeAndUrlRow, "span 2, growx, wrap");
|
||||
|
||||
shellTypeCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = shellTypeCombo.getSelectedItem();
|
||||
if (item != null) {
|
||||
controller.setShellType(String.valueOf(item));
|
||||
refreshAll.run();
|
||||
}
|
||||
});
|
||||
bindText(urlPatternField, controller::setUrlPattern);
|
||||
}
|
||||
|
||||
protected void addRandomClassSection() {
|
||||
add(randomClassNameCheck, "span 2, split 2, wrap");
|
||||
manualClassPanel.add(labeled("内存马类名", shellClassNameField), "growx");
|
||||
manualClassPanel.add(labeled("注入器类名", injectorClassNameField), "growx");
|
||||
add(manualClassPanel, "span 2, growx, hidemode 3");
|
||||
|
||||
randomClassNameCheck.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
controller.setRandomClassName(randomClassNameCheck.isSelected());
|
||||
refreshAll.run();
|
||||
});
|
||||
bindText(shellClassNameField, controller::setShellClassName);
|
||||
bindText(injectorClassNameField, controller::setInjectorClassName);
|
||||
}
|
||||
|
||||
protected JPanel labeled(String label, JComponent component) {
|
||||
JPanel p = new JPanel(new MigLayout("insets 0, fillx, wrap 1", "[grow,fill]", "[]1[]"));
|
||||
p.add(new JLabel(label), "growx");
|
||||
p.add(component, "growx");
|
||||
return p;
|
||||
}
|
||||
|
||||
protected void setComboItems(JComboBox<String> combo, List<String> items, String selected) {
|
||||
combo.removeAllItems();
|
||||
for (String item : items) combo.addItem(item);
|
||||
if (selected != null) combo.setSelectedItem(selected);
|
||||
}
|
||||
|
||||
protected void bindText(JTextField field, Consumer<String> setter) {
|
||||
field.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
changed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
changed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
changed();
|
||||
}
|
||||
|
||||
private void changed() {
|
||||
if (updating) return;
|
||||
setter.accept(field.getText());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void applyCommonState(MemShellFormState s) {
|
||||
updating = true;
|
||||
try {
|
||||
setComboItems(shellTypeCombo, controller.getShellTypesForCurrentTool(), s.getShellType());
|
||||
urlPatternField.setText(s.getUrlPattern());
|
||||
randomClassNameCheck.setSelected(s.isRandomClassName());
|
||||
boolean nextManualClassVisible = !s.isRandomClassName();
|
||||
manualClassPanel.setVisible(nextManualClassVisible);
|
||||
shellClassNameField.setText(s.getShellClassName());
|
||||
injectorClassNameField.setText(s.getInjectorClassName());
|
||||
} finally {
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class AntSwordToolPanel extends AbstractToolPanel {
|
||||
private final JTextField passField = new JTextField();
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public AntSwordToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
add(labeled("密码(可选)", passField), "span 2, growx, wrap");
|
||||
add(labeled("请求头名", headerNameField), "growx");
|
||||
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
|
||||
addRandomClassSection();
|
||||
bindText(passField, controller::setAntSwordPass);
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
try {
|
||||
passField.setText(s.getAntSwordPass());
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class BehinderToolPanel extends AbstractToolPanel {
|
||||
private final JTextField passField = new JTextField();
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public BehinderToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
add(labeled("密码(可选)", passField), "span 2, growx, wrap");
|
||||
add(labeled("请求头名", headerNameField), "growx");
|
||||
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
|
||||
addRandomClassSection();
|
||||
bindText(passField, controller::setBehinderPass);
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
try {
|
||||
passField.setText(s.getBehinderPass());
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class CommandToolPanel extends AbstractToolPanel {
|
||||
private final JPanel paramRow = new JPanel(new MigLayout("insets 0, fillx", "[grow,fill]", "[]"));
|
||||
private final JTextField paramField = new JTextField();
|
||||
private final JPanel headerRow = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[grow,fill][grow,fill]", "[]"));
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
private final JCheckBox advancedToggle = new JCheckBox("高级配置");
|
||||
private final JPanel advancedPanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8, gapy 2, wrap 2", "[grow,fill][grow,fill]", "[]"));
|
||||
private final JComboBox<String> encryptorCombo = new JComboBox<>();
|
||||
private final JComboBox<String> implCombo = new JComboBox<>();
|
||||
private final JTextField commandTemplateField = new JTextField();
|
||||
|
||||
public CommandToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
paramRow.add(labeled("参数名(可选)", paramField), "growx");
|
||||
add(paramRow, "span 2, growx, wrap, hidemode 3");
|
||||
|
||||
headerRow.add(labeled("请求头名", headerNameField), "growx");
|
||||
headerRow.add(labeled("请求头值(可选)", headerValueField), "growx");
|
||||
add(headerRow, "span 2, growx, wrap, hidemode 3");
|
||||
|
||||
add(advancedToggle, "span 2, gapy 2 0, wrap");
|
||||
advancedPanel.add(labeled("加密器", encryptorCombo), "growx");
|
||||
advancedPanel.add(labeled("实现类", implCombo), "growx");
|
||||
advancedPanel.add(labeled("命令模板(可选)", commandTemplateField), "span 2, growx");
|
||||
add(advancedPanel, "span 2, growx, wrap, hidemode 3");
|
||||
|
||||
addRandomClassSection();
|
||||
|
||||
bindText(paramField, controller::setCommandParamName);
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
bindText(commandTemplateField, controller::setCommandTemplate);
|
||||
|
||||
encryptorCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = encryptorCombo.getSelectedItem();
|
||||
controller.setEncryptor(item == null ? "" : String.valueOf(item));
|
||||
});
|
||||
implCombo.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
Object item = implCombo.getSelectedItem();
|
||||
controller.setImplementationClass(item == null ? "" : String.valueOf(item));
|
||||
});
|
||||
advancedToggle.addActionListener(e -> {
|
||||
advancedPanel.setVisible(advancedToggle.isSelected());
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
boolean layoutVisibilityChanged = false;
|
||||
try {
|
||||
String encryptor = (s.getEncryptor() == null || s.getEncryptor().trim().isEmpty())
|
||||
? (controller.getCommandEncryptors().isEmpty() ? null : controller.getCommandEncryptors().get(0))
|
||||
: s.getEncryptor();
|
||||
String impl = (s.getImplementationClass() == null || s.getImplementationClass().trim().isEmpty())
|
||||
? (controller.getCommandImplementationClasses().isEmpty() ? null : controller.getCommandImplementationClasses().get(0))
|
||||
: s.getImplementationClass();
|
||||
setComboItems(encryptorCombo, controller.getCommandEncryptors(), encryptor);
|
||||
setComboItems(implCombo, controller.getCommandImplementationClasses(), impl);
|
||||
boolean nextParamVisible = controller.isCommandParamVisible();
|
||||
boolean nextHeaderVisible = controller.isCommandHeaderVisible();
|
||||
if (paramRow.isVisible() != nextParamVisible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
if (headerRow.isVisible() != nextHeaderVisible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
paramRow.setVisible(nextParamVisible);
|
||||
headerRow.setVisible(nextHeaderVisible);
|
||||
paramField.setText(s.getCommandParamName());
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
commandTemplateField.setText(s.getCommandTemplate());
|
||||
boolean nextAdvancedVisible = advancedToggle.isSelected();
|
||||
if (advancedPanel.isVisible() != nextAdvancedVisible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
advancedPanel.setVisible(nextAdvancedVisible);
|
||||
} finally { updating = false; }
|
||||
if (layoutVisibilityChanged) {
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import com.reajason.javaweb.desktop.memshell.service.CustomClassNameParser;
|
||||
import com.reajason.javaweb.desktop.memshell.util.SwingUiUtil;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
|
||||
public class CustomToolPanel extends AbstractToolPanel {
|
||||
private final CustomClassNameParser parser;
|
||||
private final JRadioButton base64Mode = new JRadioButton("Base64", true);
|
||||
private final JRadioButton fileMode = new JRadioButton("File");
|
||||
private final JTextArea base64Area = new JTextArea(4, 40);
|
||||
private final JButton fileButton = new JButton("选择 .class 文件");
|
||||
private final JLabel fileLabel = new JLabel("未选择文件");
|
||||
private final JPanel base64Panel = new JPanel(new MigLayout("insets 0, fillx", "[grow,fill]", "[grow,fill]"));
|
||||
private final JPanel filePanel = new JPanel(new MigLayout("insets 0, fillx, gapx 8", "[grow,fill][]", "[]"));
|
||||
private final Timer parseDebounce;
|
||||
|
||||
public CustomToolPanel(MemShellFormController controller, CustomClassNameParser parser, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
this.parser = parser;
|
||||
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
group.add(base64Mode);
|
||||
group.add(fileMode);
|
||||
add(new JLabel("Shell Class"));
|
||||
JPanel radioWrap = new JPanel(new MigLayout("insets 0, gapx 8", "[][]", "[]"));
|
||||
radioWrap.add(base64Mode);
|
||||
radioWrap.add(fileMode);
|
||||
add(radioWrap, "growx, wrap");
|
||||
|
||||
base64Panel.add(new JScrollPane(base64Area), "grow");
|
||||
filePanel.add(fileLabel, "growx");
|
||||
filePanel.add(fileButton);
|
||||
add(base64Panel, "span 2, growx, gapy 1 0, wrap, hidemode 3");
|
||||
add(filePanel, "span 2, growx, gapy 1 0, wrap, hidemode 3");
|
||||
|
||||
addRandomClassSection();
|
||||
|
||||
parseDebounce = new Timer(400, this::parseAndFillClassName);
|
||||
parseDebounce.setRepeats(false);
|
||||
|
||||
base64Area.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override public void insertUpdate(DocumentEvent e) { changed(); }
|
||||
@Override public void removeUpdate(DocumentEvent e) { changed(); }
|
||||
@Override public void changedUpdate(DocumentEvent e) { changed(); }
|
||||
private void changed() {
|
||||
if (updating) return;
|
||||
controller.setShellClassBase64(base64Area.getText());
|
||||
parseDebounce.restart();
|
||||
}
|
||||
});
|
||||
|
||||
base64Mode.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
controller.setCustomInputMode("base64");
|
||||
refreshAll.run();
|
||||
});
|
||||
fileMode.addActionListener(e -> {
|
||||
if (updating) return;
|
||||
controller.setCustomInputMode("file");
|
||||
refreshAll.run();
|
||||
});
|
||||
fileButton.addActionListener(this::chooseFile);
|
||||
}
|
||||
|
||||
private void chooseFile(ActionEvent event) {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
int result = chooser.showOpenDialog(this);
|
||||
if (result != JFileChooser.APPROVE_OPTION) return;
|
||||
File file = chooser.getSelectedFile();
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
||||
String base64 = Base64.getEncoder().encodeToString(bytes);
|
||||
controller.setShellClassBase64(base64);
|
||||
fileLabel.setText(file.getName());
|
||||
String className = parser.parseClassName(bytes);
|
||||
controller.setShellClassName(className);
|
||||
refreshAll.run();
|
||||
} catch (Exception ex) {
|
||||
SwingUiUtil.showError(this, "读取自定义类失败: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAndFillClassName(ActionEvent ignored) {
|
||||
try {
|
||||
String base64 = controller.getState().getShellClassBase64();
|
||||
if (base64 == null || base64.trim().isEmpty()) return;
|
||||
String className = parser.parseClassNameFromBase64(base64);
|
||||
controller.setShellClassName(className);
|
||||
refreshAll.run();
|
||||
} catch (Exception ignoredEx) {
|
||||
// ignore parse errors while typing/pasting
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
boolean layoutVisibilityChanged = false;
|
||||
try {
|
||||
base64Mode.setSelected("base64".equalsIgnoreCase(s.getCustomInputMode()));
|
||||
fileMode.setSelected("file".equalsIgnoreCase(s.getCustomInputMode()));
|
||||
boolean nextBase64Visible = base64Mode.isSelected();
|
||||
boolean nextFileVisible = fileMode.isSelected();
|
||||
if (base64Panel.isVisible() != nextBase64Visible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
if (filePanel.isVisible() != nextFileVisible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
base64Panel.setVisible(nextBase64Visible);
|
||||
filePanel.setVisible(nextFileVisible);
|
||||
base64Area.setText(s.getShellClassBase64());
|
||||
} finally { updating = false; }
|
||||
if (layoutVisibilityChanged) {
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class GodzillaToolPanel extends AbstractToolPanel {
|
||||
private final JTextField passField = new JTextField();
|
||||
private final JTextField keyField = new JTextField();
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public GodzillaToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
add(labeled("密码(可选)", passField), "growx");
|
||||
add(labeled("密钥(可选)", keyField), "growx, wrap");
|
||||
add(labeled("请求头名", headerNameField), "growx");
|
||||
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
|
||||
addRandomClassSection();
|
||||
bindText(passField, controller::setGodzillaPass);
|
||||
bindText(keyField, controller::setGodzillaKey);
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
try {
|
||||
passField.setText(s.getGodzillaPass());
|
||||
keyField.setText(s.getGodzillaKey());
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class NeoRegToolPanel extends AbstractToolPanel {
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public NeoRegToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
add(labeled("请求头名", headerNameField), "growx");
|
||||
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
|
||||
addRandomClassSection();
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
try {
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class ProxyToolPanel extends AbstractToolPanel {
|
||||
private final JPanel headerPanel = new JPanel(new net.miginfocom.swing.MigLayout("insets 0, fillx, gapx 8", "[grow,fill][grow,fill]", "[]"));
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public ProxyToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
headerPanel.add(labeled("请求头名", headerNameField), "growx");
|
||||
headerPanel.add(labeled("请求头值(可选)", headerValueField), "growx");
|
||||
add(headerPanel, "span 2, growx, wrap, hidemode 3");
|
||||
addRandomClassSection();
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
boolean layoutVisibilityChanged = false;
|
||||
try {
|
||||
boolean nextHeaderVisible = controller.isProxyHeaderVisible();
|
||||
if (headerPanel.isVisible() != nextHeaderVisible) {
|
||||
layoutVisibilityChanged = true;
|
||||
}
|
||||
headerPanel.setVisible(nextHeaderVisible);
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
if (layoutVisibilityChanged) {
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
public interface RefreshableToolPanel {
|
||||
void refreshFromController();
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui.panel.tool;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class Suo5ToolPanel extends AbstractToolPanel {
|
||||
private final JTextField headerNameField = new JTextField();
|
||||
private final JTextField headerValueField = new JTextField();
|
||||
|
||||
public Suo5ToolPanel(MemShellFormController controller, Runnable refreshAll) {
|
||||
super(controller, refreshAll);
|
||||
add(labeled("请求头名", headerNameField), "growx");
|
||||
add(labeled("请求头值(可选)", headerValueField), "growx, wrap");
|
||||
addRandomClassSection();
|
||||
bindText(headerNameField, controller::setHeaderName);
|
||||
bindText(headerValueField, controller::setHeaderValue);
|
||||
}
|
||||
|
||||
@Override public void refreshFromController() {
|
||||
MemShellFormState s = controller.getState();
|
||||
applyCommonState(s);
|
||||
updating = true;
|
||||
try {
|
||||
headerNameField.setText(s.getHeaderName());
|
||||
headerValueField.setText(s.getHeaderValue());
|
||||
} finally { updating = false; }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.reajason.javaweb.desktop.memshell.util;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
|
||||
public final class ClipboardUtil {
|
||||
private ClipboardUtil() {}
|
||||
|
||||
public static void copyText(String text) {
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text == null ? "" : text), null);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.reajason.javaweb.desktop.memshell.util;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
public final class FileSaveUtil {
|
||||
private FileSaveUtil() {}
|
||||
|
||||
public static void saveText(Component parent, String suggestedName, String content) throws IOException {
|
||||
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter("Text", "txt"));
|
||||
if (file == null) return;
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write((content == null ? "" : content).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveBase64AsBytes(Component parent, String suggestedName, String base64, String extension) throws IOException {
|
||||
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter(extension.toUpperCase(), extension));
|
||||
if (file == null) return;
|
||||
byte[] bytes = Base64.getDecoder().decode(base64 == null ? "" : base64);
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveBytes(Component parent, String suggestedName, byte[] bytes, String extension) throws IOException {
|
||||
File file = chooseFile(parent, suggestedName, new FileNameExtensionFilter(extension.toUpperCase(), extension));
|
||||
if (file == null) return;
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public static File chooseFile(Component parent, String suggestedName, FileNameExtensionFilter filter) {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setSelectedFile(new File(suggestedName));
|
||||
chooser.setFileFilter(filter);
|
||||
int result = chooser.showSaveDialog(parent);
|
||||
if (result != JFileChooser.APPROVE_OPTION) {
|
||||
return null;
|
||||
}
|
||||
return chooser.getSelectedFile();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.reajason.javaweb.desktop.memshell.util;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public final class SwingUiUtil {
|
||||
private SwingUiUtil() {}
|
||||
|
||||
public static JPanel titledPanel(String title, LayoutManager layout) {
|
||||
JPanel panel = new JPanel(layout);
|
||||
panel.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createTitledBorder(title),
|
||||
BorderFactory.createEmptyBorder(6, 6, 6, 6)));
|
||||
return panel;
|
||||
}
|
||||
|
||||
public static void showError(Component parent, String message) {
|
||||
JOptionPane.showMessageDialog(parent, message, "错误", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
public static void showInfo(Component parent, String message) {
|
||||
JOptionPane.showMessageDialog(parent, message, "提示", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
public static void runOnEdt(Runnable runnable) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
SwingUtilities.invokeLater(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.reajason.javaweb.desktop.memshell.validation;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MemShellValidator {
|
||||
public static class Result {
|
||||
private final Map<String, String> fieldErrors = new LinkedHashMap<>();
|
||||
|
||||
public boolean isValid() {
|
||||
return fieldErrors.isEmpty();
|
||||
}
|
||||
|
||||
public Map<String, String> getFieldErrors() {
|
||||
return fieldErrors;
|
||||
}
|
||||
|
||||
public void add(String field, String message) {
|
||||
fieldErrors.put(field, message);
|
||||
}
|
||||
|
||||
public String firstMessage() {
|
||||
return fieldErrors.values().stream().findFirst().orElse("");
|
||||
}
|
||||
}
|
||||
|
||||
public Result validate(MemShellFormState s) {
|
||||
Result r = new Result();
|
||||
required(r, "server", s.getServer(), "请选择服务类型");
|
||||
required(r, "serverVersion", s.getServerVersion(), "请选择服务版本");
|
||||
required(r, "shellTool", s.getShellTool(), "请选择内存马工具");
|
||||
required(r, "shellType", s.getShellType(), "请选择内存马挂载类型");
|
||||
required(r, "packingMethod", s.getPackingMethod(), "请选择打包方式");
|
||||
|
||||
if (needsUrlPattern(s.getShellType()) && isInvalidUrl(s.getUrlPattern())) {
|
||||
r.add("urlPattern", "请使用具体 URL 路径,不能为 / 或 /*");
|
||||
}
|
||||
if ("Custom".equals(s.getShellTool()) && (s.getShellClassBase64() == null || s.getShellClassBase64().trim().isEmpty())) {
|
||||
r.add("shellClassBase64", "自定义内存马 Class(Base64) 不能为空");
|
||||
}
|
||||
if ("TongWeb".equals(s.getServer()) && "Valve".equals(s.getShellType()) && "Unknown".equals(s.getServerVersion())) {
|
||||
r.add("serverVersion", "TongWeb Valve 模式需要指定服务版本");
|
||||
}
|
||||
if ("Jetty".equals(s.getServer())
|
||||
&& ("Handler".equals(s.getShellType()) || "JakartaHandler".equals(s.getShellType()))
|
||||
&& "Unknown".equals(s.getServerVersion())) {
|
||||
r.add("serverVersion", "Jetty Handler 模式需要指定服务版本");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean needsUrlPattern(String shellType) {
|
||||
if (shellType == null || shellType.trim().isEmpty()) return false;
|
||||
if (shellType.startsWith("Agent")) return false;
|
||||
return shellType.endsWith("Servlet") ||
|
||||
shellType.endsWith("ControllerHandler") ||
|
||||
shellType.equals("HandlerMethod") ||
|
||||
shellType.equals("HandlerFunction") ||
|
||||
shellType.endsWith("WebSocket");
|
||||
}
|
||||
|
||||
public boolean isInvalidUrl(String urlPattern) {
|
||||
return urlPattern == null || urlPattern.trim().isEmpty() || "/".equals(urlPattern) || "/*".equals(urlPattern) || !urlPattern.startsWith("/");
|
||||
}
|
||||
|
||||
private void required(Result r, String field, String value, String message) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
r.add(field, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.reajason.javaweb.desktop.memshell.controller;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MemShellFormControllerTest {
|
||||
|
||||
@Test
|
||||
void shouldAdjustJdkForSpringWebFluxAndResetDependentFields() {
|
||||
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
controller.setServerVersion("Unknown");
|
||||
controller.setUrlPattern("/abc");
|
||||
|
||||
controller.setServer("SpringWebFlux");
|
||||
|
||||
assertEquals("52", controller.getState().getTargetJdkVersion());
|
||||
assertEquals("", controller.getState().getUrlPattern());
|
||||
assertNotNull(controller.getState().getShellTool());
|
||||
assertFalse(controller.getState().getShellTool().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFilterAgentPackersForAgentShellType() {
|
||||
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
controller.setShellType("AgentFilterChain");
|
||||
List<?> filtered = controller.getFilteredPackers();
|
||||
assertFalse(filtered.isEmpty());
|
||||
assertTrue(controller.getFilteredPackers().stream().allMatch(p -> p.getName().startsWith("Agent")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldToggleRandomClassNameAndRestoreValues() {
|
||||
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
controller.setRandomClassName(false);
|
||||
controller.setShellClassName("a.b.C");
|
||||
controller.setInjectorClassName("x.y.Z");
|
||||
|
||||
controller.setRandomClassName(true);
|
||||
assertEquals("", controller.getState().getShellClassName());
|
||||
assertEquals("", controller.getState().getInjectorClassName());
|
||||
|
||||
controller.setRandomClassName(false);
|
||||
assertEquals("a.b.C", controller.getState().getShellClassName());
|
||||
assertEquals("x.y.Z", controller.getState().getInjectorClassName());
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.reajason.javaweb.desktop.memshell.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CustomClassNameParserTest {
|
||||
@Test
|
||||
void shouldParseClassNameFromClassBytesAndBase64() throws Exception {
|
||||
CustomClassNameParser parser = new CustomClassNameParser();
|
||||
byte[] bytes = readOwnClassBytes();
|
||||
String expected = this.getClass().getName();
|
||||
|
||||
assertEquals(expected, parser.parseClassName(bytes));
|
||||
assertEquals(expected, parser.parseClassNameFromBase64(Base64.getEncoder().encodeToString(bytes)));
|
||||
}
|
||||
|
||||
private byte[] readOwnClassBytes() throws IOException {
|
||||
String resource = "/" + this.getClass().getName().replace('.', '/') + ".class";
|
||||
InputStream in = this.getClass().getResourceAsStream(resource);
|
||||
assertNotNull(in);
|
||||
try (InputStream is = in; ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
byte[] buf = new byte[4096];
|
||||
int n;
|
||||
while ((n = is.read(buf)) != -1) out.write(buf, 0, n);
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.reajason.javaweb.desktop.memshell.service;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class GenerationServiceTest {
|
||||
@Test
|
||||
void shouldGenerateTomcatGodzillaListenerWithBase64Packer() {
|
||||
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
controller.setServer("Tomcat");
|
||||
controller.setShellTool("Godzilla");
|
||||
controller.setShellType("Listener");
|
||||
controller.setPacker("Base64");
|
||||
|
||||
GenerationService service = new GenerationService();
|
||||
DesktopMemShellGenerateResult result = service.generate(controller.getState().copy());
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals("Base64", result.getPackMethod());
|
||||
assertNotNull(result.getPackResult());
|
||||
assertFalse(result.getPackResult().isEmpty());
|
||||
assertNotNull(result.getMemShellResult().getShellBytesBase64Str());
|
||||
assertNotNull(result.getMemShellResult().getInjectorBytesBase64Str());
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui;
|
||||
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MemShellGeneratorFrameLayoutTest {
|
||||
|
||||
@Test
|
||||
void shouldUseStackedLayoutWithoutSplitPaneAndKeepGenerateButton() throws Exception {
|
||||
Assumptions.assumeFalse(GraphicsEnvironment.isHeadless(), "Headless environment");
|
||||
|
||||
final MemShellGeneratorFrame[] ref = new MemShellGeneratorFrame[1];
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ref[0] = new MemShellGeneratorFrame();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
final JComponent[] contentRef = new JComponent[1];
|
||||
final JButton[] buttonRef = new JButton[1];
|
||||
final Dimension[] minSizeRef = new Dimension[1];
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
contentRef[0] = ref[0].getMainContentPanel();
|
||||
buttonRef[0] = ref[0].getGenerateButton();
|
||||
minSizeRef[0] = ref[0].getMinimumSize();
|
||||
}
|
||||
});
|
||||
|
||||
assertNotNull(contentRef[0]);
|
||||
assertFalse(contentRef[0] instanceof JSplitPane, "main content should not be a JSplitPane");
|
||||
assertNotNull(buttonRef[0]);
|
||||
assertTrue(buttonRef[0].isEnabled());
|
||||
assertNotNull(minSizeRef[0]);
|
||||
assertTrue(minSizeRef[0].width >= 1180);
|
||||
assertTrue(minSizeRef[0].height >= 900);
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ref[0] != null) {
|
||||
ref[0].dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.reajason.javaweb.desktop.memshell.ui;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.controller.MemShellFormController;
|
||||
import com.reajason.javaweb.desktop.memshell.model.DesktopMemShellGenerateResult;
|
||||
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
|
||||
import com.reajason.javaweb.desktop.memshell.service.GenerationService;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.panel.ResultPanel;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ResultPanelBasicInfoLayoutTest {
|
||||
|
||||
@Test
|
||||
void shouldUseStructuredNoScrollBasicInfo() throws Exception {
|
||||
MemShellFormController controller = new MemShellFormController(new ConfigCatalogService(), new MemShellValidator());
|
||||
controller.setServer("Tomcat");
|
||||
controller.setShellTool("Godzilla");
|
||||
controller.setShellType("Listener");
|
||||
controller.setPacker("Base64");
|
||||
DesktopMemShellGenerateResult result = new GenerationService().generate(controller.getState().copy());
|
||||
|
||||
final ResultPanel panel = new ResultPanel();
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panel.showResult(result);
|
||||
}
|
||||
});
|
||||
|
||||
JComponent basic = panel.getBasicInfoComponent();
|
||||
assertNotNull(basic);
|
||||
assertTrue(basic instanceof JPanel);
|
||||
assertFalse(containsScrollPane(basic), "basic info section should not contain JScrollPane");
|
||||
assertTrue(containsText(panel, "服务类型"));
|
||||
assertTrue(containsText(panel, "内存马功能"));
|
||||
assertTrue(containsText(panel, "注入器类名"));
|
||||
assertTrue(containsText(panel, "内存马类名"));
|
||||
}
|
||||
|
||||
private boolean containsScrollPane(Component c) {
|
||||
if (c instanceof JScrollPane) return true;
|
||||
if (c instanceof Container) {
|
||||
for (Component child : ((Container) c).getComponents()) {
|
||||
if (containsScrollPane(child)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsText(Component c, String expected) {
|
||||
if (c instanceof JLabel) {
|
||||
String text = ((JLabel) c).getText();
|
||||
if (text != null && text.contains(expected)) return true;
|
||||
}
|
||||
if (c instanceof JTextField) {
|
||||
String text = ((JTextField) c).getText();
|
||||
if (text != null && text.contains(expected)) return true;
|
||||
}
|
||||
if (c instanceof Container) {
|
||||
for (Component child : ((Container) c).getComponents()) {
|
||||
if (containsText(child, expected)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.reajason.javaweb.desktop.memshell.validation;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MemShellValidatorTest {
|
||||
private final MemShellValidator validator = new MemShellValidator();
|
||||
|
||||
@Test
|
||||
void shouldRejectGenericUrlPatternWhenRequired() {
|
||||
MemShellFormState s = new MemShellFormState();
|
||||
s.setPackingMethod("Base64");
|
||||
s.setShellType("Servlet");
|
||||
s.setUrlPattern("/*");
|
||||
|
||||
MemShellValidator.Result result = validator.validate(s);
|
||||
assertFalse(result.isValid());
|
||||
assertTrue(result.getFieldErrors().containsKey("urlPattern"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowNoUrlPatternForListener() {
|
||||
MemShellFormState s = new MemShellFormState();
|
||||
s.setPackingMethod("Base64");
|
||||
s.setShellType("Listener");
|
||||
s.setUrlPattern("/*");
|
||||
|
||||
MemShellValidator.Result result = validator.validate(s);
|
||||
assertTrue(result.isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRequireCustomShellBase64ForCustomTool() {
|
||||
MemShellFormState s = new MemShellFormState();
|
||||
s.setPackingMethod("Base64");
|
||||
s.setShellTool("Custom");
|
||||
s.setShellClassBase64("");
|
||||
|
||||
MemShellValidator.Result result = validator.validate(s);
|
||||
assertFalse(result.isValid());
|
||||
assertTrue(result.getFieldErrors().containsKey("shellClassBase64"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRequireJettyVersionForHandler() {
|
||||
MemShellFormState s = new MemShellFormState();
|
||||
s.setPackingMethod("Base64");
|
||||
s.setServer("Jetty");
|
||||
s.setShellType("Handler");
|
||||
s.setServerVersion("Unknown");
|
||||
s.setUrlPattern("/x");
|
||||
|
||||
MemShellValidator.Result result = validator.validate(s);
|
||||
assertFalse(result.isValid());
|
||||
assertTrue(result.getFieldErrors().containsKey("serverVersion"));
|
||||
}
|
||||
}
|
||||
@@ -50,3 +50,4 @@ include("memshell-agent:memshell-agent-attacher")
|
||||
include("memshell-agent:memshell-agent-asm")
|
||||
include("memshell-agent:memshell-agent-javassist")
|
||||
include("memshell-agent:memshell-agent-bytebuddy")
|
||||
include("desktop:java-swing-gui")
|
||||
|
||||
Reference in New Issue
Block a user