This commit is contained in:
2026-04-04 21:04:10 +02:00
parent 6635c5fb20
commit 5d6626609d
3 changed files with 52 additions and 19 deletions
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<option name="previewPanelProviderInfo">
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
</option>
</component>
</project>
+1 -19
View File
@@ -10,11 +10,9 @@ import net.bytebuddy.description.method.*
import net.bytebuddy.asm.Advice import net.bytebuddy.asm.Advice
import java.lang.instrument.* import java.lang.instrument.*
import java.util.jar.JarFile import java.util.jar.JarFile
import java.nio.file.Path
import java.io.File import java.io.File
@Suppress("unused") object Agent { @Suppress("unused") object Agent {
private const val KNOT = "net.fabricmc.loader.impl.launch.knot"
@JvmStatic fun premain(args: String?, inst: Instrumentation?) = attach(inst!!) @JvmStatic fun premain(args: String?, inst: Instrumentation?) = attach(inst!!)
@JvmStatic fun agentmain(args: String?, inst: Instrumentation?) = attach(inst!!) @JvmStatic fun agentmain(args: String?, inst: Instrumentation?) = attach(inst!!)
val injections = listOf(Tick::class, Render::class, Gui::class) val injections = listOf(Tick::class, Render::class, Gui::class)
@@ -32,27 +30,11 @@ import java.io.File
} }
} }
private fun getKnotDelegate(): Any? {
val base = Class.forName("$KNOT.KnotClassLoader")
val delegate = base.getDeclaredField("delegate")
val threads = Thread.getAllStackTraces().keys
val loaders = threads.mapNotNull { it.contextClassLoader }
val knot = loaders.firstOrNull { it.javaClass == base }
return delegate.apply { isAccessible = true }.get(knot)
}
private fun exposeToKnot(jar: Path) {
val delegate = getKnotDelegate()
val base = Class.forName("$KNOT.KnotClassDelegate")
val source = base.getMethod("addCodeSource", Path::class.java)
source.apply { isAccessible = true }.invoke(delegate, jar)
}
private fun attach(inst: Instrumentation) { private fun attach(inst: Instrumentation) {
val domain = Client::class.java.protectionDomain val domain = Client::class.java.protectionDomain
val location = File(domain.codeSource.location.toURI()) val location = File(domain.codeSource.location.toURI())
inst.appendToSystemClassLoaderSearch(JarFile(location)) inst.appendToSystemClassLoaderSearch(JarFile(location))
runCatching { exposeToKnot(location.toPath()) } Knot(location.toPath()).register(inst)
var builder: AgentBuilder = Default() var builder: AgentBuilder = Default()
builder = builder.disableClassFormatChanges() builder = builder.disableClassFormatChanges()
@@ -0,0 +1,43 @@
package dev.zerozipp.loader
import java.lang.instrument.*
import java.security.ProtectionDomain
import java.nio.file.Path
class Knot(private val jar: Path) : ClassFileTransformer {
private val knot = "net.fabricmc.loader.impl.launch.knot"
private var registry = mutableSetOf<Instrumentation>()
override fun transform(classLoader: ClassLoader, className: String,
redefined: Class<*>, domain: ProtectionDomain, bytes: ByteArray
): ByteArray? = null.also { load(classLoader) }
fun register(inst: Instrumentation) {
val result = runCatching { expose(jar) }
if(result.isSuccess) return
inst.addTransformer(this)
registry.add(inst)
}
private fun delegate() = run {
val base = Class.forName("$knot.KnotClassLoader")
val delegate = base.getDeclaredField("delegate")
val threads = Thread.getAllStackTraces().keys
val loaders = threads.mapNotNull { it.contextClassLoader }
val knot = loaders.firstOrNull { it.javaClass == base }
delegate.apply { isAccessible = true }.get(knot)
}
private fun expose(jar: Path) = delegate().let {
val base = Class.forName("$knot.KnotClassDelegate")
val source = base.getMethod("addCodeSource", Path::class.java)
source.apply { isAccessible = true }.invoke(it, jar)
}
private fun load(loader: ClassLoader) {
val knotLoader = "$knot.KnotClassLoader"
if(loader.javaClass.name != knotLoader) return
registry.forEach { it.removeTransformer(this) }
runCatching { expose(jar) }
registry.clear()
}
}