package org.tzd.debug; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.api.dog.agent.VirtualMachine; import org.tzd.debug.util.GlobalObjects; import javax.tools.*; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import java.util.stream.Collectors; /** * 对类进行监控和debug操作 * @author tzdwindows 7 */ public class ClassDebug { private static final Logger logger = LogManager.getLogger(ClassDebug.class); private static Instrumentation instrumentation; /** * 监控类加载 * @param monitor 监控器 */ public static void monitoringLoading(LoadingMonitor monitor) { if (instrumentation != null) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { monitor.load(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); return classfileBuffer; } }); } else { logger.error("Instrumentation is null"); try { VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); instrumentation = vm.getInstrumentation(); } catch (Exception e) { logger.error("Failed to attach to VM: {}", e.getMessage()); } monitoringLoading(monitor); } } public static Instrumentation getInstrumentation() { if (instrumentation == null) { try { VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); instrumentation = vm.getInstrumentation(); } catch (Exception e) { logger.error("Failed to attach to VM: {}", e.getMessage()); } } return instrumentation; } /** * 执行动态代码 * @param code 要执行的代码 * @return 执行结果(空字符串表示成功,否则为错误信息) */ public static String executeCode(String code) { GlobalObjects globalObjects = new GlobalObjects(); globalObjects.instrumentation = instrumentation; // 1. 预处理代码:替换print/printf为System.out String processedCode = code.trim(); if (processedCode.startsWith("print(") && processedCode.endsWith(");")) { processedCode = "System.out.println" + processedCode.substring("print".length()); } else if (processedCode.startsWith("printf(") && processedCode.endsWith(");")) { processedCode = "System.out.printf" + processedCode.substring("printf".length()); } // 2. 构建完整类代码 String className = "DynamicExecutedCode"; String fullCode = "package dynamic;\n" + // 添加包声明避免冲突 "import org.tzd.debug.util.GlobalObjects;import java.util.*;\n" + "public class " + className + " {\n" + " public static GlobalObjects global;\n" + // 静态字段接收全局对象 " public static void run() {\n" + " try {\n" + // 添加try-catch捕获用户代码异常 processedCode + "\n" + " } catch (Throwable t) {\n" + " throw new RuntimeException(t);\n" + // 包装异常以保持堆栈 " }\n" + " }\n" + "}"; Path tempDir = null; try { // 3. 创建临时目录和源文件 tempDir = Files.createTempDirectory("dynamicCode"); Path sourceDir = tempDir.resolve("dynamic"); Files.createDirectories(sourceDir); Path sourceFile = sourceDir.resolve(className + ".java"); Files.write(sourceFile, fullCode.getBytes(StandardCharsets.UTF_8)); // 4. 编译代码 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); if (compiler == null) { return "错误:找不到Java编译器。请使用JDK运行此程序。"; } DiagnosticCollector diagnostics = new DiagnosticCollector<>(); try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) { String[] compileOptions = {"-d", tempDir.toString(), "-cp", System.getProperty("java.class.path")}; Iterable compilationUnits = fileManager.getJavaFileObjects(sourceFile); JavaCompiler.CompilationTask task = compiler.getTask( null, fileManager, diagnostics, Arrays.asList(compileOptions), null, compilationUnits ); boolean success = task.call(); if (!success) { StringBuilder errorMsg = new StringBuilder("编译错误:\n"); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { errorMsg.append(String.format( "Line %d: %s\n", diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault()) )); } return errorMsg.toString(); } } // 5. 加载并执行 URLClassLoader classLoader = new URLClassLoader( new URL[]{tempDir.toUri().toURL()}, ClassDebug.class.getClassLoader() ); Class loadedClass = classLoader.loadClass("dynamic." + className); // 注入全局对象 Field globalField = loadedClass.getDeclaredField("global"); globalField.set(null, globalObjects); Method runMethod = loadedClass.getMethod("run"); runMethod.invoke(null); return ""; } catch (Throwable e) { // 6. 异常处理 Throwable cause = e; while (cause.getCause() != null) { cause = cause.getCause(); } StringWriter sw = new StringWriter(); cause.printStackTrace(new PrintWriter(sw)); return "运行时错误:" + cause.getMessage() + "\n堆栈跟踪:\n" + sw.toString(); } finally { // 7. 清理资源 if (tempDir != null) { try { Files.walk(tempDir) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } catch (IOException ignored) {} } } } public static void main(String[] args) { executeCode("JOptionPane.showMessageDialog(null, \"普通对话框\");"); } /** * 获取已加载的类 * @return 已加载的类 */ public static Class[] getLoadedClasses() { if (instrumentation == null){ try { VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true); instrumentation = vm.getInstrumentation(); } catch (Exception e) { logger.error("Failed to attach to VM: {}", e.getMessage()); } } return instrumentation.getAllLoadedClasses(); } /** * 获取类的方法与字段 * @param clazz 类名 * @return 类的方法与字段 */ public static String getMethodAndField(String clazz) { try { Class aClass = Class.forName(clazz); String methods = Arrays.stream(aClass.getDeclaredMethods()) .map(Method::toString) .collect(Collectors.joining("\n")); String fields = Arrays.stream(aClass.getDeclaredFields()) .map(Field::toString) .collect(Collectors.joining("\n")); return "方法:\n" + methods + "\n\n字段:\n" + fields; } catch (ClassNotFoundException e) { throw new RuntimeException("Class not found: " + clazz, e); } } public interface LoadingMonitor { void load(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer); } }