[FIX] InjectClassAcceptor, остался сериализ Map<String, String>

This commit is contained in:
Zaxar163 2020-01-22 14:21:56 +01:00
parent 6ec2c035cf
commit ef6616dfda
No known key found for this signature in database
GPG key ID: 1FE4F2E1F053831B
6 changed files with 157 additions and 21 deletions

View file

@ -1,33 +1,57 @@
package pro.gravit.launchserver.asm;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import pro.gravit.launchserver.binary.BuildContext;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import pro.gravit.launcher.LauncherInject;
import pro.gravit.launcher.LauncherInjectionConstructor;
public class InjectClassAcceptor {
public static final Class<?>[] primitives = new Class<?>[] { java.lang.Boolean.class, java.lang.Character.class,
java.lang.Byte.class, java.lang.Short.class, java.lang.Integer.class, java.lang.Long.class,
java.lang.Float.class, java.lang.Double.class, java.lang.String.class };
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
private final Map<String, Object> values;
public static final List<Class<?>> zPrimitivesList = Arrays.asList(primitives);
public static final String INJ_DESC = Type.getDescriptor(LauncherInject.class);
public static void checkMap(Map<String,Object> object) {
if (!zPrimitivesList.containsAll(object.values()))
throw new IllegalArgumentException("Only primitives in values...");
public InjectClassAcceptor(Map<String, Object> values) {
this.values = values;
}
private static final List<Class<?>> zPrimitivesList = Arrays.asList(java.lang.Boolean.class, java.lang.Character.class,
java.lang.Byte.class, java.lang.Short.class, java.lang.Integer.class, java.lang.Long.class,
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
private static final String INJ_DESC = Type.getDescriptor(LauncherInject.class);
private static final String INJ_C_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
private static final List<String> cPrimitivesList = Arrays.asList("I", "V", "Z", "B", "C", "S", "D", "F", "J", Type.getDescriptor(String.class));
public static void visit(ClassNode cn, Map<String, Object> object) {
cn.fields.stream().filter(e -> e.invisibleAnnotations != null)
private static void visit(ClassNode cn, Map<String, Object> object) {
MethodNode clinit = cn.methods.stream().filter(methodNode ->
"<clinit>".equals(methodNode.name)).findFirst().orElseGet(() -> {
MethodNode ret = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, "<clinit>", "()V", null, null);
ret.instructions.add(new InsnNode(Opcodes.RETURN));
cn.methods.add(ret);
return ret;
});
List<MethodNode> constructors = cn.methods.stream().filter(e -> "<init>".equals(e.name)).collect(Collectors.toList());
MethodNode init = constructors.stream().filter(e -> e.invisibleAnnotations.stream().filter(f -> INJ_C_DESC.equals(f.desc)).findFirst()
.isPresent()).findFirst().orElseGet(() -> constructors.stream().filter(e -> e.desc.equals("()V")).findFirst().orElse(null));
cn.fields.stream().filter(e -> e.invisibleAnnotations != null)
.filter(e -> !e.invisibleAnnotations.isEmpty() && e.invisibleAnnotations.stream().anyMatch(f -> f.desc.equals(INJ_DESC))).forEach(e -> {
// Notice that fields that will be used with this algo should not have default
// value by = ...;
@ -47,8 +71,114 @@ public void visit(final String name, final Object value) {
});
if (valueName.get() == null)
throw new IllegalArgumentException("Annotation should always contains 'value' key");
if (object.containsKey(valueName.get()))
e.value = object.get(valueName.get());
if (object.containsKey(valueName.get())) {
Object val = object.get(valueName.get());
if ((e.access & Opcodes.ACC_STATIC) != 0)
if (cPrimitivesList.contains(e.desc) && zPrimitivesList.contains(val.getClass()))
e.value = val;
else {
List<FieldInsnNode> nodes = Arrays.stream(clinit.instructions.toArray()).filter(p -> p instanceof FieldInsnNode && p.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
.filter(p -> p.owner.equals(cn.name) && p.name.equals(e.name) && p.desc.equals(e.desc)).collect(Collectors.toList());
InsnList injector = new InsnList();
pushInjector(injector, val, e);
if (nodes.isEmpty()) {
injector.insert(new InsnNode(Opcodes.ICONST_0));
injector.add(new FieldInsnNode(Opcodes.PUTSTATIC, cn.name, e.name, e.desc));
Arrays.stream(clinit.instructions.toArray()).filter(p -> p.getOpcode() == Opcodes.RETURN).forEach(p -> clinit.instructions.insertBefore(p, injector));
} else
for (FieldInsnNode node : nodes) clinit.instructions.insertBefore(node, injector);
}
else {
if (init == null) throw new IllegalArgumentException("Not found init in target: " + cn.name);
List<FieldInsnNode> nodes = Arrays.stream(init.instructions.toArray()).filter(p -> p instanceof FieldInsnNode && p.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
.filter(p -> p.owner.equals(cn.name) && p.name.equals(e.name) && p.desc.equals(e.desc)).collect(Collectors.toList());
InsnList injector = new InsnList();
pushInjector(injector, val, e);
if (nodes.isEmpty()) {
injector.insert(new VarInsnNode(Opcodes.ALOAD, 0));
injector.insert(new InsnNode(Opcodes.ICONST_0));
injector.add(new FieldInsnNode(Opcodes.PUTSTATIC, cn.name, e.name, e.desc));
Arrays.stream(init.instructions.toArray()).filter(p -> p.getOpcode() == Opcodes.RETURN).forEach(p -> clinit.instructions.insertBefore(p, injector));
} else
for (FieldInsnNode node : nodes) init.instructions.insertBefore(node, injector);
}
}
});
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void pushInjector(InsnList injector, Object val, FieldNode e) {
injector.add(new InsnNode(Opcodes.POP));
if (e.desc.equals("Z")) {
if ((Boolean) val) injector.add(new InsnNode(Opcodes.ICONST_1));
else injector.add(new InsnNode(Opcodes.ICONST_0));
} else if (e.desc.equals("C")) {
injector.add(NodeUtils.push(((Number) val).intValue()));
injector.add(new InsnNode(Opcodes.I2C));
} else if (e.desc.equals("B")) {
injector.add(NodeUtils.push(((Number) val).intValue()));
injector.add(new InsnNode(Opcodes.I2B));
} else if (e.desc.equals("S")) {
injector.add(NodeUtils.push(((Number) val).intValue()));
injector.add(new InsnNode(Opcodes.I2S));
} else if (e.desc.equals("I")) {
injector.add(NodeUtils.push(((Number) val).intValue()));
} else if (e.desc.equals("[B")) {
serializebArr(injector, (byte[]) val);
} else if (e.desc.equals("Ljava/util/List;")) {
if (((List) val).isEmpty()) {
injector.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
injector.add(new InsnNode(Opcodes.DUP));
injector.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
} else {
Class<?> c = ((List) val).get(0).getClass();
if (c == byte[].class)
serializeListbArr(injector, (List<byte[]>) val);
else if (c == String.class)
serializeListString(injector, (List<String>) val);
else
throw new UnsupportedOperationException("Unsupported class" + c.getName());
}
} else {
if (!cPrimitivesList.contains(e.desc) || !zPrimitivesList.contains(val.getClass()))
throw new UnsupportedOperationException("Unsupported class");
injector.add(new LdcInsnNode(val));
}
// TODO Map<String,String>
}
private static void serializebArr(InsnList injector, byte[] val) {
injector.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false));
injector.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(val)));
injector.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false));
}
private static void serializeListbArr(InsnList inj, List<byte[]> val) {
inj.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
inj.add(new InsnNode(Opcodes.DUP)); // +1
inj.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
for (byte[] value : val) {
inj.add(new InsnNode(Opcodes.DUP)); // +1-1
serializebArr(inj, value);
inj.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true));
inj.add(new InsnNode(Opcodes.POP));
}
}
private static void serializeListString(InsnList inj, List<String> val) {
inj.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
inj.add(new InsnNode(Opcodes.DUP)); // +1
inj.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
for (String value : val) {
inj.add(new InsnNode(Opcodes.DUP)); // +1-1
inj.add(NodeUtils.getSafeStringInsnList(value));
inj.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true));
inj.add(new InsnNode(Opcodes.POP));
}
}
@Override
public void transform(ClassNode cn, String classname, BuildContext context) {
visit(cn, values);
}
}

View file

@ -1,7 +1,5 @@
package pro.gravit.launchserver.dao;
import pro.gravit.launcher.hwid.HWID;
import java.util.List;
import java.util.UUID;

View file

@ -6,7 +6,6 @@
import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.OshiHWID;
import pro.gravit.launchserver.dao.HwidDAO;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.dao.UserHWID;
import javax.persistence.EntityManager;

View file

@ -3,11 +3,8 @@
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.OshiHWID;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.dao.UserDAO;
import pro.gravit.launchserver.dao.UserHWID;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;

View file

@ -1,6 +1,5 @@
package pro.gravit.launchserver.dao.impl;
import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.OshiHWID;
import pro.gravit.launchserver.dao.UserHWID;

View file

@ -0,0 +1,13 @@
package pro.gravit.launcher;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(CLASS)
@Target(CONSTRUCTOR)
public @interface LauncherInjectionConstructor {
}