fix class version below 50 is not supported

This commit is contained in:
Haowei Wen 2022-07-10 04:25:24 +08:00
parent 4862db0572
commit 5059a55789
12 changed files with 278 additions and 250 deletions

View file

@ -1,71 +0,0 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package moe.yushi.authlibinjector.transform;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASM9;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.NEW;
import java.util.Optional;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
class CallbackMetafactoryTransformer implements TransformUnit {
@Override
public Optional<ClassVisitor> transform(ClassLoader classLoader, String className, ClassVisitor writer, TransformContext context) {
return Optional.of(new ClassVisitor(ASM9, writer) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
MethodVisitor mv = super.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC,
CallbackSupport.METAFACTORY_NAME,
CallbackSupport.METAFACTORY_SIGNATURE,
null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "java/lang/invoke/ConstantCallSite");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
context.markModified();
}
});
}
@Override
public String toString() {
return "Callback Metafactory Transformer";
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -16,17 +16,42 @@
*/
package moe.yushi.authlibinjector.transform;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.RETURN;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
public final class CallbackSupport {
final class CallbackSupport {
private CallbackSupport() {
}
static final String METAFACTORY_NAME = "__authlibinjector_metafactory";
static final String METAFACTORY_SIGNATURE = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;";
private static final String METAFACTORY_NAME = "__authlibinjector_metafactory";
private static final String METAFACTORY_SIGNATURE = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;";
private static Method findCallbackMethod(Class<?> owner, String methodName) {
for (Method method : owner.getDeclaredMethods()) {
@ -40,11 +65,123 @@ public final class CallbackSupport {
throw new IllegalArgumentException("No such method: " + methodName);
}
public static void invoke(TransformContext ctx, MethodVisitor mv, Class<?> owner, String methodName) {
ctx.requireMinimumClassVersion(50);
ctx.upgradeClassVersion(51);
static void callWithInvokeDynamic(MethodVisitor mv, Class<?> owner, String methodName, TransformContext ctx) {
String descriptor = Type.getMethodDescriptor(findCallbackMethod(owner, methodName));
mv.visitInvokeDynamicInsn(methodName, descriptor, ctx.acquireCallbackMetafactory(), owner.getName());
Handle callbackMetafactory = new Handle(
H_INVOKESTATIC,
ctx.getClassName().replace('.', '/'),
CallbackSupport.METAFACTORY_NAME,
CallbackSupport.METAFACTORY_SIGNATURE,
ctx.isInterface());
mv.visitInvokeDynamicInsn(methodName, descriptor, callbackMetafactory, owner.getName());
}
static void callWithIntermediateMethod(MethodVisitor mv0, Class<?> owner, String methodName, TransformContext ctx) {
Method callbackMethod = findCallbackMethod(owner, methodName);
String descriptor = Type.getMethodDescriptor(callbackMethod);
String intermediateMethod = "__authlibinjector_intermediate__" + owner.getName().replace('.', '_') + "__" + methodName;
mv0.visitMethodInsn(INVOKESTATIC, ctx.getClassName().replace('.', '/'), intermediateMethod, descriptor, ctx.isInterface());
ctx.addGeneratedMethod(intermediateMethod, cv -> {
int paramNum = callbackMethod.getParameterCount();
Class<?>[] paramTypes = callbackMethod.getParameterTypes();
Class<?> returnType = callbackMethod.getReturnType();
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, intermediateMethod, descriptor, null, null);
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "publicLookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false);
mv.visitLdcInsn(owner.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
mv.visitLdcInsn(methodName);
pushType(mv, returnType);
mv.visitLdcInsn(paramNum);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");
for (int i = 0; i < paramNum; i++) {
mv.visitInsn(DUP);
mv.visitLdcInsn(i);
pushType(mv, paramTypes[i]);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "methodType", "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
for (int i = 0; i < paramNum; i++) {
Class<?> type = paramTypes[i];
if (type == boolean.class || type == byte.class || type == char.class || type == short.class || type == int.class) {
mv.visitVarInsn(ILOAD, i);
} else if (type == long.class) {
mv.visitVarInsn(LLOAD, i);
} else if (type == float.class) {
mv.visitVarInsn(FLOAD, i);
} else if (type == double.class) {
mv.visitVarInsn(DLOAD, i);
} else {
mv.visitVarInsn(ALOAD, i);
}
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact", descriptor, false);
if (returnType == void.class) {
mv.visitInsn(RETURN);
} else if (returnType == boolean.class || returnType == byte.class || returnType == char.class || returnType == short.class || returnType == int.class) {
mv.visitInsn(IRETURN);
} else if (returnType == long.class) {
mv.visitInsn(LRETURN);
} else if (returnType == float.class) {
mv.visitInsn(FRETURN);
} else if (returnType == double.class) {
mv.visitInsn(DRETURN);
} else {
mv.visitInsn(ARETURN);
}
mv.visitMaxs(-1, -1);
mv.visitEnd();
});
}
private static void pushType(MethodVisitor mv, Class<?> type) {
if (type.isPrimitive()) {
if (type == boolean.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
} else if (type == byte.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
} else if (type == char.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
} else if (type == short.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
} else if (type == int.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
} else if (type == float.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
} else if (type == long.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
} else if (type == double.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
} else if (type == void.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
}
} else {
mv.visitLdcInsn(Type.getType(type));
}
}
static void insertMetafactory(ClassVisitor visitor) {
MethodVisitor mv = visitor.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC,
CallbackSupport.METAFACTORY_NAME,
CallbackSupport.METAFACTORY_SIGNATURE,
null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "java/lang/invoke/ConstantCallSite");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}

View file

@ -22,20 +22,24 @@ import static moe.yushi.authlibinjector.util.Logging.Level.DEBUG;
import static moe.yushi.authlibinjector.util.Logging.Level.INFO;
import static moe.yushi.authlibinjector.util.Logging.Level.WARNING;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static org.objectweb.asm.Opcodes.ASM9;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.Config;
public class ClassTransformer implements ClassFileTransformer {
@ -50,8 +54,6 @@ public class ClassTransformer implements ClassFileTransformer {
private class TransformContextImpl implements TransformContext {
public boolean modifiedMark;
public int minVersionMark = -1;
public int upgradedVersionMark = -1;
public boolean callbackMetafactoryRequested = false;
@Override
@ -59,35 +61,40 @@ public class ClassTransformer implements ClassFileTransformer {
modifiedMark = true;
}
@Override
public void requireMinimumClassVersion(int version) {
if (this.minVersionMark < version) {
this.minVersionMark = version;
}
}
@Override
public void upgradeClassVersion(int version) {
if (this.upgradedVersionMark < version) {
this.upgradedVersionMark = version;
}
}
@Override
public Handle acquireCallbackMetafactory() {
this.callbackMetafactoryRequested = true;
return new Handle(
H_INVOKESTATIC,
className.replace('.', '/'),
CallbackSupport.METAFACTORY_NAME,
CallbackSupport.METAFACTORY_SIGNATURE,
TransformHandle.this.isInterface());
}
@Override
public List<String> getStringConstants() {
return TransformHandle.this.getStringConstants();
}
@Override
public String getClassName() {
return className;
}
@Override
public boolean isInterface() {
return TransformHandle.this.isInterface();
}
@Override
public void invokeCallback(MethodVisitor mv, Class<?> owner, String methodName) {
boolean useInvokeDynamic = (getClassVersion() & 0xffff) >= 50;
if (useInvokeDynamic) {
addCallbackMetafactory = true;
CallbackSupport.callWithInvokeDynamic(mv, owner, methodName, this);
} else {
CallbackSupport.callWithIntermediateMethod(mv, owner, methodName, this);
}
}
@Override
public void addGeneratedMethod(String name, Consumer<ClassVisitor> generator) {
if (generatedMethods == null) {
generatedMethods = new LinkedHashMap<>();
}
generatedMethods.put(name, generator);
}
}
private final String className;
@ -97,9 +104,8 @@ public class ClassTransformer implements ClassFileTransformer {
private List<String> cachedConstants;
private List<TransformUnit> appliedTransformers;
private int minVersion = -1;
private int upgradedVersion = -1;
private boolean addCallbackMetafactory = false;
private Map<String, Consumer<ClassVisitor>> generatedMethods;
public TransformHandle(ClassLoader classLoader, String className, byte[] classBuffer) {
this.className = className;
@ -123,6 +129,11 @@ public class ClassTransformer implements ClassFileTransformer {
return cachedConstants;
}
private int getClassVersion() {
ClassReader reader = getClassReader();
return reader.readInt(reader.getItem(1) - 7);
}
public void accept(TransformUnit... units) {
long t0 = System.nanoTime();
@ -168,43 +179,77 @@ public class ClassTransformer implements ClassFileTransformer {
appliedTransformers = new ArrayList<>();
appliedTransformers.add(units[i]);
if (ctx.minVersionMark > this.minVersion) {
this.minVersion = ctx.minVersionMark;
}
if (ctx.upgradedVersionMark > this.upgradedVersion) {
this.upgradedVersion = ctx.upgradedVersionMark;
}
this.addCallbackMetafactory |= ctx.callbackMetafactoryRequested;
modified = true;
}
if (modified) {
classBuffer = writer.toByteArray();
cachedClassReader = null;
cachedConstants = null;
updateClassBuffer(writer.toByteArray());
}
}
private void injectCallbackMetafactory() {
log(DEBUG, "Adding callback metafactory");
int classVersion = getClassVersion();
int majorVersion = classVersion & 0xffff;
int newVersion;
if (majorVersion < 51) {
newVersion = 51;
log(DEBUG, "Upgrading class version from " + classVersion + " to " + newVersion);
} else {
newVersion = classVersion;
}
ClassReader reader = getClassReader();
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new ClassVisitor(ASM9, writer) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(newVersion, access, name, signature, superName, interfaces);
CallbackSupport.insertMetafactory(this);
}
};
reader.accept(visitor, 0);
updateClassBuffer(writer.toByteArray());
}
private void injectGeneratedMethods() {
ClassReader reader = getClassReader();
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new ClassVisitor(ASM9, writer) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
for (Entry<String, Consumer<ClassVisitor>> el : generatedMethods.entrySet()) {
log(DEBUG, "Adding generated method [" + el.getKey() + "]");
el.getValue().accept(this);
}
}
};
reader.accept(visitor, 0);
updateClassBuffer(writer.toByteArray());
}
private void updateClassBuffer(byte[] buf) {
classBuffer = buf;
cachedClassReader = null;
cachedConstants = null;
}
public Optional<byte[]> finish() {
if (appliedTransformers == null || appliedTransformers.isEmpty()) {
return Optional.empty();
} else {
if (addCallbackMetafactory) {
accept(new CallbackMetafactoryTransformer());
}
if (minVersion == -1 && upgradedVersion == -1) {
return Optional.of(classBuffer);
} else {
try {
accept(new ClassVersionTransformUnit(minVersion, upgradedVersion));
return Optional.of(classBuffer);
} catch (ClassVersionException e) {
log(WARNING, "Skipping [" + className + "], " + e.getMessage());
return Optional.empty();
}
}
}
if (addCallbackMetafactory) {
injectCallbackMetafactory();
}
if (generatedMethods != null) {
injectGeneratedMethods();
}
return Optional.of(classBuffer);
}
public List<TransformUnit> getAppliedTransformers() {

View file

@ -1,23 +0,0 @@
/*
* Copyright (C) 2020 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package moe.yushi.authlibinjector.transform;
class ClassVersionException extends RuntimeException {
public ClassVersionException(String message) {
super(message);
}
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package moe.yushi.authlibinjector.transform;
import static moe.yushi.authlibinjector.util.Logging.log;
import static moe.yushi.authlibinjector.util.Logging.Level.DEBUG;
import static org.objectweb.asm.Opcodes.ASM9;
import java.util.Optional;
import org.objectweb.asm.ClassVisitor;
class ClassVersionTransformUnit implements TransformUnit {
private final int minVersion;
private final int upgradedVersion;
public ClassVersionTransformUnit(int minVersion, int upgradedVersion) {
this.minVersion = minVersion;
this.upgradedVersion = upgradedVersion;
}
@Override
public Optional<ClassVisitor> transform(ClassLoader classLoader, String className, ClassVisitor writer, TransformContext context) {
return Optional.of(new ClassVisitor(ASM9, writer) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
int major = version & 0xffff;
if (minVersion != -1 && major < minVersion) {
throw new ClassVersionException("class version (" + major + ") is lower than required(" + minVersion + ")");
}
if (upgradedVersion != -1 && major < upgradedVersion) {
log(DEBUG,"Upgrading class version from " + major + " to " + upgradedVersion);
version = upgradedVersion;
context.markModified();
}
super.visit(version, access, name, signature, superName, interfaces);
}
});
}
@Override
public String toString() {
return "Class File Version Transformer";
}
}

View file

@ -17,17 +17,21 @@
package moe.yushi.authlibinjector.transform;
import java.util.List;
import org.objectweb.asm.Handle;
import java.util.function.Consumer;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
public interface TransformContext {
String getClassName();
boolean isInterface();
void markModified();
void requireMinimumClassVersion(int version);
void upgradeClassVersion(int version);
Handle acquireCallbackMetafactory();
List<String> getStringConstants();
void invokeCallback(MethodVisitor mv, Class<?> owner, String methodName);
void addGeneratedMethod(String name, Consumer<ClassVisitor> generator);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -37,7 +37,6 @@ import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
@ -232,7 +231,7 @@ public class AuthlibLogInterceptor implements TransformUnit {
super.visitCode();
super.visitLdcInsn(Type.getType("L" + className.replace('.', '/') + ";"));
super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false);
CallbackSupport.invoke(ctx, mv, AuthlibLogInterceptor.class, "onClassLoading");
ctx.invokeCallback(mv, AuthlibLogInterceptor.class, "onClassLoading");
ctx.markModified();
}
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -25,7 +25,6 @@ import java.util.Optional;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
@ -59,7 +58,7 @@ public class ConcatenateURLTransformUnit implements TransformUnit {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
CallbackSupport.invoke(ctx, mv, ConcatenateURLTransformUnit.class, "concatenateURL");
ctx.invokeCallback(mv, ConcatenateURLTransformUnit.class, "concatenateURL");
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -33,7 +33,6 @@ import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.AuthlibInjector;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
import moe.yushi.authlibinjector.util.WeakIdentityHashMap;
@ -120,7 +119,7 @@ public class MC52974_1710Workaround {
if (opcode == ARETURN) {
ctx.markModified();
super.visitInsn(DUP);
CallbackSupport.invoke(ctx, mv, MC52974_1710Workaround.class, "markGameProfile");
ctx.invokeCallback(mv, MC52974_1710Workaround.class, "markGameProfile");
}
super.visitInsn(opcode);
}
@ -163,7 +162,7 @@ public class MC52974_1710Workaround {
super.visitMethodInsn(INVOKESTATIC, "net/minecraft/server/MinecraftServer", "func_71276_C", "()Lnet/minecraft/server/MinecraftServer;", false);
}
super.visitLdcInsn(isNotchName ? 1 : 0);
CallbackSupport.invoke(ctx, mv, MC52974_1710Workaround.class, "accessGameProfile");
ctx.invokeCallback(mv, MC52974_1710Workaround.class, "accessGameProfile");
super.visitTypeInsn(CHECKCAST, "com/mojang/authlib/GameProfile");
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -31,7 +31,6 @@ import java.util.stream.Stream;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
@ -51,7 +50,7 @@ public class MainArgumentsTransformer implements TransformUnit {
ctx.markModified();
super.visitVarInsn(ALOAD, 0);
CallbackSupport.invoke(ctx, mv, MainArgumentsTransformer.class, "processMainArguments");
ctx.invokeCallback(mv, MainArgumentsTransformer.class, "processMainArguments");
super.visitVarInsn(ASTORE, 0);
}
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Haowei Wen <yushijinhun@gmail.com> and contributors
* Copyright (C) 2022 Haowei Wen <yushijinhun@gmail.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -27,7 +27,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
@ -64,6 +63,7 @@ public class SkinWhitelistTransformUnit implements TransformUnit {
@CallbackMethod
public static boolean isWhitelistedDomain(String url) {
System.out.println(url);
String domain;
try {
domain = new URI(url).getHost();
@ -103,7 +103,7 @@ public class SkinWhitelistTransformUnit implements TransformUnit {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
CallbackSupport.invoke(ctx, mv, SkinWhitelistTransformUnit.class, "isWhitelistedDomain");
ctx.invokeCallback(mv, SkinWhitelistTransformUnit.class, "isWhitelistedDomain");
mv.visitInsn(IRETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();

View file

@ -16,10 +16,11 @@
*/
package moe.yushi.authlibinjector.transform.support;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
import static moe.yushi.authlibinjector.util.Logging.Level.DEBUG;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASM9;
import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import java.lang.invoke.MethodHandle;
@ -32,10 +33,8 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import moe.yushi.authlibinjector.transform.CallbackMethod;
import moe.yushi.authlibinjector.transform.CallbackSupport;
import moe.yushi.authlibinjector.transform.TransformContext;
import moe.yushi.authlibinjector.transform.TransformUnit;
import moe.yushi.authlibinjector.util.Logging;
@ -46,12 +45,14 @@ public class YggdrasilKeyTransformUnit implements TransformUnit {
public static final List<PublicKey> PUBLIC_KEYS = new CopyOnWriteArrayList<>();
@CallbackMethod
public static boolean verifyPropertySignature(Object property, PublicKey mojangKey, MethodHandle verifyAction) throws Throwable {
if ((boolean) verifyAction.invoke(property, mojangKey)) {
public static boolean verifyPropertySignature(Object property, PublicKey mojangKey) throws Throwable {
MethodHandle verifyAction = publicLookup().bind(property, "isSignatureValid", methodType(boolean.class, PublicKey.class));
if ((boolean) verifyAction.invokeExact(mojangKey)) {
return true;
}
for (PublicKey customKey : PUBLIC_KEYS) {
if ((boolean) verifyAction.invoke(property, customKey)) {
if ((boolean) verifyAction.invokeExact(customKey)) {
return true;
}
}
@ -101,8 +102,7 @@ public class YggdrasilKeyTransformUnit implements TransformUnit {
&& "isSignatureValid".equals(name)
&& "(Ljava/security/PublicKey;)Z".equals(descriptor)) {
ctx.markModified();
super.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, owner, name, descriptor, isInterface));
CallbackSupport.invoke(ctx, this, YggdrasilKeyTransformUnit.class, "verifyPropertySignature");
ctx.invokeCallback(this, YggdrasilKeyTransformUnit.class, "verifyPropertySignature");
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
@ -126,7 +126,7 @@ public class YggdrasilKeyTransformUnit implements TransformUnit {
mv.visitMethodInsn(INVOKEVIRTUAL, "com/mojang/authlib/properties/Property", "getValue", "()Ljava/lang/String;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "com/mojang/authlib/properties/Property", "getSignature", "()Ljava/lang/String;", false);
CallbackSupport.invoke(ctx, mv, YggdrasilKeyTransformUnit.class, "verifyPropertySignatureNew");
ctx.invokeCallback(mv, YggdrasilKeyTransformUnit.class, "verifyPropertySignatureNew");
mv.visitInsn(IRETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();