目标:在 Spine 编辑器窗口内部加一个自己的视图面板(按钮 + 文字显示), 点击读取场景对象,并像原生面板一样出现在 Views 菜单、可停靠可折叠。 本轮打通: 1. 编辑器在我们自己的进程里完整启动 - 类加载器必须是 URLClassLoader 本身(自检要求) - Editor.main 必须在 AWT EDT 上调用 - jvm.cfg 第 21 行要以两个空格结尾 + -DSpineJava - Editor.D:I 版本号、fonts/、/tzax 资源、Java8 版 launcher.Launcher 替身 2. 3.8.99 全量解密 → 可读源码 - 字符串 17251/17461(伪造同名调用帧对齐 key) - 混淆动态派发还原 53871 处(调引导方法取 MemberName) 3. 自建视图 - agent 在内存里给 us 枚举加 spineView 常量(不改磁盘,避开完整性自检) - 面板 Actor 由外部反射填进 us.spineView.D - 复用已有图标做别名(不新建图片资源) 最终 "Spine View" 出现在 Views 菜单,可与 Tree 并排停靠、折叠展开 另外: - ViewPatcher/ViewAgent:视图枚举与面板挂载的字节码插桩 - DecAll/ObMap/rewrite_source:字符串解密、派发还原、可读源码生成 - 阶段四-视图面板挂载.md:完整记录做法与踩过的坑 不纳入版本管理:rea/(独立仓库)、tools 下的 JDK/Ghidra 工具链、 Spine.app 与 spine-lab 副本、从载荷派生的批量产物。
61 lines
2.3 KiB
Java
61 lines
2.3 KiB
Java
import java.lang.instrument.Instrumentation;
|
||
import java.security.Permission;
|
||
|
||
/**
|
||
* 极小 javaagent:拦 System.exit,把调用栈打出来。
|
||
*
|
||
* 目的:3.8.99 编辑器在"必须用捆绑运行时"那道检查后会退出,
|
||
* 拿到 checkExit 的调用栈就知道检查写在哪、校验的是什么,不用再猜。
|
||
*
|
||
* 用法:-javaagent:rea-agent.jar
|
||
*/
|
||
public final class ReaAgent {
|
||
public static void premain(String arguments, Instrumentation instrumentation) {
|
||
System.err.println("[rea-agent] premain 已加载");
|
||
|
||
// JVM 退出时把主线程等的位置打出来。进程是"静默退出"还是"崩在别处",
|
||
// 看一次栈就够,不用再猜。
|
||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||
@Override
|
||
public void run() {
|
||
System.err.println("[rea-agent] JVM 正在退出,当前线程栈:");
|
||
for (java.util.Map.Entry<Thread, StackTraceElement[]> entry
|
||
: Thread.getAllStackTraces().entrySet()) {
|
||
Thread thread = entry.getKey();
|
||
if (!thread.isAlive()) continue;
|
||
System.err.println("[rea-agent] 线程 " + thread.getName()
|
||
+ (thread.isDaemon() ? " (daemon)" : ""));
|
||
for (StackTraceElement frame : entry.getValue()) {
|
||
System.err.println("[rea-agent] " + frame);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||
@Override
|
||
public void uncaughtException(Thread thread, Throwable error) {
|
||
System.err.println("[rea-agent] 未捕获异常,线程 " + thread.getName());
|
||
error.printStackTrace(System.err);
|
||
}
|
||
});
|
||
|
||
System.setSecurityManager(new SecurityManager() {
|
||
@Override
|
||
public void checkPermission(Permission permission) {
|
||
// 全部放行,只观察。
|
||
}
|
||
|
||
@Override
|
||
public void checkExit(int status) {
|
||
Throwable trace = new Throwable("checkExit(" + status + ")");
|
||
StringBuilder builder = new StringBuilder("[rea-agent] System.exit(" + status + ") 调用栈:");
|
||
for (StackTraceElement frame : trace.getStackTrace()) {
|
||
builder.append("\n ").append(frame);
|
||
}
|
||
System.err.println(builder.toString());
|
||
// 不抛异常 = 允许退出,这样行为和原来一致。
|
||
}
|
||
});
|
||
}
|
||
}
|