Skip to content
This repository was archived by the owner on May 12, 2024. It is now read-only.

Commit e996d2e

Browse files
committed
Added bootstrap launcher and java agent
1 parent 1262ce5 commit e996d2e

8 files changed

Lines changed: 341 additions & 0 deletions

File tree

bootstrap/build.gradle

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
plugins {
2+
id "java"
3+
id "application"
4+
}
5+
6+
configurations {
7+
include
8+
9+
implementation.extendsFrom include
10+
api.extendsFrom include
11+
}
12+
13+
dependencies {
14+
include project(":impl-classtransform")
15+
16+
include "net.lenni0451:Reflect:1.2.4"
17+
}
18+
19+
application {
20+
mainClass = "net.raphimc.javadowngrader.bootstrap.Main"
21+
}
22+
23+
jar {
24+
dependsOn configurations.include
25+
from {
26+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
27+
configurations.include.collect {
28+
zipTree(it)
29+
}
30+
} {
31+
exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA"
32+
}
33+
34+
manifest {
35+
attributes(
36+
"Main-Class": application.mainClass,
37+
"Premain-Class": "net.raphimc.javadowngrader.bootstrap.AgentMain",
38+
"Agent-Class": "net.raphimc.javadowngrader.bootstrap.AgentMain",
39+
"Can-Redefine-Classes": "true",
40+
"Can-Retransform-Classes": "true",
41+
"Multi-Release": "true"
42+
)
43+
}
44+
}
45+
46+
project.tasks.withType(PublishToMavenRepository).forEach {
47+
it.enabled = false
48+
}

bootstrap/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
maven_name=JavaDowngrader-Bootstrap
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.bootstrap;
19+
20+
import net.lenni0451.classtransform.TransformerManager;
21+
import net.raphimc.javadowngrader.bootstrap.util.FileSystemUtil;
22+
import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer;
23+
import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil;
24+
import net.raphimc.javadowngrader.runtime.RuntimeRoot;
25+
import net.raphimc.javadowngrader.util.Constants;
26+
27+
import java.io.IOException;
28+
import java.io.UncheckedIOException;
29+
import java.lang.instrument.Instrumentation;
30+
import java.nio.file.FileSystem;
31+
import java.nio.file.Files;
32+
import java.nio.file.Path;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
import java.util.stream.Stream;
36+
37+
public class AgentMain {
38+
39+
public static void premain(String args, Instrumentation instrumentation) throws Throwable {
40+
agentmain(args, instrumentation);
41+
}
42+
43+
public static void agentmain(String args, Instrumentation instrumentation) throws Throwable {
44+
final Map<String, byte[]> runtimeClasses = new HashMap<>();
45+
try (FileSystem runtimeRootFs = FileSystemUtil.getOrCreateFileSystem(RuntimeRoot.class.getResource("").toURI())) {
46+
final Path runtimeRoot = runtimeRootFs.getPath('/' + Constants.JAVADOWNGRADER_RUNTIME_PACKAGE);
47+
try (Stream<Path> stream = Files.walk(runtimeRoot)) {
48+
stream.filter(Files::isRegularFile)
49+
.filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT))
50+
.forEach(path -> {
51+
try {
52+
String classFilePath = ClassNameUtil.slashName(path);
53+
if (classFilePath.startsWith("/")) {
54+
classFilePath = classFilePath.substring(1);
55+
}
56+
runtimeClasses.put(ClassNameUtil.toClassName(classFilePath), Files.readAllBytes(path));
57+
} catch (IOException e) {
58+
throw new UncheckedIOException(e);
59+
}
60+
});
61+
}
62+
}
63+
64+
instrumentation.addTransformer(new RuntimeClassesAdderTransformer(runtimeClasses));
65+
final TransformerManager transformerManager = new TransformerManager(new InstrumentationClassProvider(instrumentation));
66+
transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer(transformerManager));
67+
transformerManager.hookInstrumentation(instrumentation);
68+
}
69+
70+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.bootstrap;
19+
20+
import net.lenni0451.classtransform.utils.tree.BasicClassProvider;
21+
22+
import java.lang.instrument.Instrumentation;
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
26+
public class InstrumentationClassProvider extends BasicClassProvider {
27+
28+
private final Instrumentation instrumentation;
29+
private final Set<ClassLoader> classLoaders = new HashSet<>();
30+
31+
public InstrumentationClassProvider(Instrumentation instrumentation) {
32+
this.instrumentation = instrumentation;
33+
}
34+
35+
@Override
36+
public byte[] getClass(String name) throws ClassNotFoundException {
37+
for (int i = 0; i < 2; i++) {
38+
for (ClassLoader classLoader : this.classLoaders) {
39+
try {
40+
this.classLoader = classLoader;
41+
return super.getClass(name);
42+
} catch (ClassNotFoundException ignored) {
43+
}
44+
}
45+
this.refreshClassLoaders();
46+
}
47+
48+
throw new ClassNotFoundException(name);
49+
}
50+
51+
private void refreshClassLoaders() {
52+
this.classLoaders.clear();
53+
for (Class<?> clazz : this.instrumentation.getAllLoadedClasses()) {
54+
this.classLoaders.add(clazz.getClassLoader());
55+
}
56+
}
57+
58+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.bootstrap;
19+
20+
import net.lenni0451.classtransform.TransformerManager;
21+
import net.lenni0451.classtransform.additionalclassprovider.LazyFileClassProvider;
22+
import net.lenni0451.classtransform.utils.loader.InjectionClassLoader;
23+
import net.lenni0451.classtransform.utils.tree.BasicClassProvider;
24+
import net.lenni0451.reflect.ClassLoaders;
25+
import net.lenni0451.reflect.Methods;
26+
import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer;
27+
28+
import java.io.File;
29+
import java.net.URL;
30+
import java.util.Arrays;
31+
import java.util.Collections;
32+
import java.util.jar.JarFile;
33+
34+
public class Main {
35+
36+
public static void main(String[] args) throws ClassNotFoundException {
37+
if (args.length < 1) {
38+
final String jarPath = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
39+
final String jarName = jarPath.substring(jarPath.lastIndexOf('/') + 1);
40+
System.out.println("Usage: java -jar " + jarName + " <jar> [args]");
41+
return;
42+
}
43+
44+
final File jarFile = new File(args[0]);
45+
if (!jarFile.isFile()) {
46+
System.err.println("File not found: " + jarFile.getAbsolutePath());
47+
return;
48+
}
49+
50+
URL jarUrl;
51+
String mainClass;
52+
try {
53+
jarUrl = jarFile.toURI().toURL();
54+
55+
try (final JarFile jar = new JarFile(jarFile)) {
56+
mainClass = jar.getManifest().getMainAttributes().getValue("Main-Class");
57+
}
58+
if (mainClass == null) throw new RuntimeException("No main class found");
59+
} catch (Throwable e) {
60+
throw new RuntimeException("Invalid jar file", e);
61+
}
62+
63+
final URL[] systemClassPath = ClassLoaders.getSystemClassPath();
64+
final URL[] urls = Arrays.copyOf(systemClassPath, systemClassPath.length + 1);
65+
urls[urls.length - 1] = jarUrl;
66+
args = Arrays.copyOfRange(args, 1, args.length);
67+
68+
final TransformerManager transformerManager = new TransformerManager(new LazyFileClassProvider(Collections.singletonList(jarFile), new BasicClassProvider()));
69+
transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer(transformerManager));
70+
final InjectionClassLoader injectionClassLoader = new InjectionClassLoader(transformerManager, urls);
71+
Thread.currentThread().setContextClassLoader(injectionClassLoader);
72+
Methods.invoke(null, Methods.getDeclaredMethod(injectionClassLoader.loadClass(mainClass), "main", String[].class), (Object) args);
73+
}
74+
75+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.bootstrap;
19+
20+
import net.lenni0451.reflect.ClassLoaders;
21+
22+
import java.lang.instrument.ClassFileTransformer;
23+
import java.security.ProtectionDomain;
24+
import java.util.HashSet;
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
public class RuntimeClassesAdderTransformer implements ClassFileTransformer {
29+
30+
private final Map<String, byte[]> runtimeClasses;
31+
private final Set<ClassLoader> injectedLoaders = new HashSet<>();
32+
33+
public RuntimeClassesAdderTransformer(final Map<String, byte[]> runtimeClasses) {
34+
this.runtimeClasses = runtimeClasses;
35+
}
36+
37+
@Override
38+
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
39+
if (loader != null && !this.injectedLoaders.contains(loader)) {
40+
this.injectedLoaders.add(loader);
41+
for (Map.Entry<String, byte[]> entry : this.runtimeClasses.entrySet()) {
42+
ClassLoaders.defineClass(loader, entry.getKey(), entry.getValue());
43+
}
44+
}
45+
46+
return null;
47+
}
48+
49+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.bootstrap.util;
19+
20+
import java.io.IOException;
21+
import java.net.URI;
22+
import java.nio.file.FileSystem;
23+
import java.nio.file.FileSystemNotFoundException;
24+
import java.nio.file.FileSystems;
25+
import java.util.Collections;
26+
27+
public class FileSystemUtil {
28+
29+
public static FileSystem getOrCreateFileSystem(final URI uri) throws IOException {
30+
FileSystem fileSystem;
31+
try {
32+
fileSystem = FileSystems.getFileSystem(uri);
33+
} catch (FileSystemNotFoundException e) {
34+
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
35+
}
36+
return fileSystem;
37+
}
38+
39+
}

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pluginManagement {
88
rootProject.name = "JavaDowngrader"
99

1010
include(":standalone")
11+
include(":bootstrap")
1112
include(":runtime-dep")
1213
include(":impl-classtransform")
1314
include(":gradle-plugin")

0 commit comments

Comments
 (0)