Skip to content

Commit

Permalink
feat: support debug mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ReaJason committed Dec 9, 2024
1 parent e28ddf9 commit 76f3ed4
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 68 deletions.
17 changes: 10 additions & 7 deletions generator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ idea {
}

dependencies {
implementation 'net.bytebuddy:byte-buddy:1.15.1'
implementation 'net.bytebuddy:byte-buddy:1.+'
implementation 'javax.servlet:javax.servlet-api:3.0.1'
implementation 'javax.websocket:javax.websocket-api:1.1'
implementation 'org.java-websocket:Java-WebSocket:1.5.7'
implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0'
// implementation fileTree('libs')

implementation 'commons-io:commons-io:2.18.0'
implementation 'org.apache.commons:commons-lang3:3.17.0'
implementation 'commons-codec:commons-codec:1.17.1'
implementation 'commons-io:commons-io:2.+'
implementation 'org.apache.commons:commons-lang3:3.+'
implementation 'commons-codec:commons-codec:1.+'

implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'ch.qos.logback:logback-classic:1.5.12'
implementation 'com.squareup.okhttp3:okhttp:4.+'
implementation 'ch.qos.logback:logback-classic:1.+'

implementation('org.apache.tomcat:tomcat-catalina:8.5.58') {
exclude group: 'org.apache.tomcat', module: 'tomcat-api'
Expand All @@ -50,7 +52,8 @@ dependencies {
}


testImplementation platform('org.junit:junit-bom:5.11.3')
testImplementation platform('org.junit:junit-bom:5.+')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "org.mockito:mockito-core:5.+"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @author ReaJason
*/
public class ByPassJdkModuleInterceptor {
public class ByPassJavaModuleInterceptor {
@Advice.OnMethodEnter
public static void enter(@Advice.Origin Class<?> clazz) {
try {
Expand Down Expand Up @@ -61,7 +61,7 @@ public static DynamicType.Builder<?> extend(DynamicType.Builder<?> builder) {
.intercept(FixedValue.originType())
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("byPassJdkModule"),
Advice.to(ByPassJdkModuleInterceptor.class)))
Advice.to(ByPassJavaModuleInterceptor.class)))
.invokable(isTypeInitializer())
.intercept(MethodCall.invoke(named("byPassJdkModule")));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.reajason.javaweb.buddy;

import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import static net.bytebuddy.jar.asm.Opcodes.INVOKEVIRTUAL;
import static net.bytebuddy.jar.asm.Opcodes.POP;

/**
* Debug 信息打印移除器,目前仅支持移除 System.out.println() - (printf 还不支持) 和 e.printStackTrace()
* @author ReaJason
*/
public class LogRemoveMethodVisitor implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
public static final LogRemoveMethodVisitor INSTANCE = new LogRemoveMethodVisitor();

public static DynamicType.Builder<?> extend(DynamicType.Builder<?> builder) {
return builder.visit(
new AsmVisitorWrapper.ForDeclaredMethods()
.method(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE));
}

@NotNull
@Override
public MethodVisitor wrap(@NotNull TypeDescription instrumentedType,
@NotNull MethodDescription instrumentedMethod,
@NotNull MethodVisitor methodVisitor,
@NotNull Implementation.Context implementationContext,
@NotNull TypePool typePool,
int writerFlags,
int readerFlags) {
return new MethodVisitor(Opcodes.ASM9, methodVisitor) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println"))
|| (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace"))) {
String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";");
for (String arg : args) {
if (StringUtils.isNotBlank(arg)) {
super.visitInsn(POP);
}
}
super.visitInsn(POP);
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class ShellConfig {
@Builder.Default
private int targetJreVersion = Constants.DEFAULT_VERSION;

/**
* 是否需要移除模块限制
*/
@Builder.Default
private boolean byPassJavaModule = false;

/**
* 是否开启混淆
*/
Expand All @@ -49,12 +55,16 @@ public class ShellConfig {
@Builder.Default
private boolean debug = false;

public boolean isDebugOff(){
return !debug;
}


public boolean isJakarta() {
return StringUtils.containsIgnoreCase(shellType, "jakarta");
}

public boolean needByPassJdkModule() {
return targetJreVersion >= Opcodes.V9;
public boolean needByPassJavaModule() {
return byPassJavaModule || targetJreVersion >= Opcodes.V9;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.reajason.javaweb.memsell;

import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.CommandConfig;
Expand Down Expand Up @@ -33,6 +34,10 @@ public static byte[] generate(ShellConfig config, CommandConfig shellConfig) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}

if (config.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}

try (DynamicType.Unloaded<?> make = builder.make()) {
return make.getBytes();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.reajason.javaweb.memsell;

import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.GodzillaConfig;
Expand Down Expand Up @@ -39,6 +40,10 @@ public static byte[] generate(ShellConfig config, GodzillaConfig shellConfig) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}

if (config.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}

try (DynamicType.Unloaded<?> make = builder.make()) {
return make.getBytes();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memsell;

import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.InjectorConfig;
import com.reajason.javaweb.config.ShellConfig;
Expand Down Expand Up @@ -34,8 +35,12 @@ public static byte[] generate(ShellConfig config, InjectorConfig injectorConfig)
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));

if (config.needByPassJdkModule()) {
builder = ByPassJdkModuleInterceptor.extend(builder);
if (config.needByPassJavaModule()) {
builder = ByPassJavaModuleInterceptor.extend(builder);
}

if (config.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}

try (DynamicType.Unloaded<?> make = builder.make()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,6 @@ public JettyFilterInjector() {

}

public String getFilterName(String className) {
if (className.contains(".")) {
int lastDotIndex = className.lastIndexOf(".");
return className.substring(lastDotIndex + 1);
} else {
return className;
}
}

public void addFilter(Object context, Object magicFilter) {
Class<?> filterClass = magicFilter.getClass();
try {
Expand Down Expand Up @@ -122,7 +113,8 @@ List<Object> getContext() {
} catch (Exception ignored) {
}
}
System.out.printf("contextSize: %s%n", contexts.size());
String log = String.format("contextSize: %s%n", contexts.size());
System.out.println(log);
return contexts;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.reajason.javaweb.buddy;

import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;

import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.condition.JRE.JAVA_17;

/**
* @author ReaJason
* @since 2024/12/7
*/
class ByPassJavaModuleInterceptorTest {
static class TestClass {
static {
System.out.println("TestClass");
}

public TestClass() {
}


public String hello() {
return "hello";
}
}

@Test
@SneakyThrows
@EnabledOnJre(JAVA_17)
void testByPassModule() {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
assertThrows(InaccessibleObjectException.class, () -> {
defineClass.setAccessible(true);
});
ByPassJavaModuleInterceptor.enter(this.getClass());
assertDoesNotThrow(() -> {
defineClass.setAccessible(true);
});
}

@Test
@SneakyThrows
void test() {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(TestClass.class)
.name("com.reajason.javaweb.buddy.ByPassJdkModuleInterceptorTest$TestClass1");
builder = ByPassJavaModuleInterceptor.extend(builder);
try (DynamicType.Unloaded<?> make = builder.make()) {
byte[] bytes = make.getBytes();
Files.write(Paths.get("build", "classes", "TestClass1.class"), bytes);
Class<?> loaded = make.load(Thread.currentThread().getContextClassLoader()).getLoaded();
assertNotNull(loaded.getDeclaredMethod("byPassJdkModule"));
}
}

}

This file was deleted.

Loading

0 comments on commit 76f3ed4

Please sign in to comment.