Read for information...

Now using ASM manipulation to remove invalid methods on load
Fixed imports
Fixed Chat Components being used in 1.12
Fixed tab complete showing args for disguise options you can't use
Disguise option permissions now demand a parameter to be the method name
Falling block disguises are now only usable with blocks
LibsDisguises command now tab completes the new options
Libs Disguises command lets you create a vanilla compatible item string
If a vehicle is disguised as a vehicle, don't give no gravity
Fixed horse disguise using almost random values for its flagwatcher settings
Renamed horse disguise setMouthOpen to setEating
Slightly better string for premium info jar location
Skip attributes packets if using older ProtocolLib jar
Don't cancel entity death if entity is dead
Improved disguise permissions checking
Fixed time parameter not being attributed properly
This commit is contained in:
libraryaddict
2020-02-19 12:57:39 +13:00
parent 668eec641e
commit 897a6629ae
65 changed files with 1205 additions and 513 deletions

View File

@@ -1,129 +0,0 @@
package me.libraryaddict.disguise.utilities.reflection;
import me.libraryaddict.disguise.disguisetypes.FlagWatcher;
import me.libraryaddict.disguise.disguisetypes.watchers.MushroomCowWatcher;
import me.libraryaddict.disguise.utilities.DisguiseSound;
import me.libraryaddict.disguise.utilities.DisguiseSoundEnums;
import me.libraryaddict.disguise.utilities.LibsPremium;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Sound;
import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* Created by libraryaddict on 13/02/2020.
*/
public class CompileMethods {
public static void main(String[] args) {
doMethods();
doSounds();
}
private static void doSounds() {
List<String> list = new ArrayList<>();
for (DisguiseSoundEnums s : DisguiseSoundEnums.values()) {
StringBuilder sound = new StringBuilder(s.name());
for (DisguiseSound.SoundType type : DisguiseSound.SoundType.values()) {
sound.append(":");
int i = 0;
for (Map.Entry<Sound, DisguiseSound.SoundType> values : s.getDisguiseSounds().entrySet()) {
if (values.getValue() != type) {
continue;
}
if (i++ > 0) {
sound.append(",");
}
sound.append(values.getKey().name());
}
}
list.add(sound.toString());
}
File soundsFile = new File("target/classes/ANTI_PIRACY_ENCODED_WITH_SOUNDS");
try (PrintWriter writer = new PrintWriter(soundsFile, "UTF-8")) {
writer.write(StringUtils.join(list, "\n"));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private static void doMethods() {
ArrayList<Class<?>> classes = ClassGetter
.getClassesForPackage(FlagWatcher.class, "me.libraryaddict.disguise.disguisetypes.watchers");
classes.add(FlagWatcher.class);
classes.add(MushroomCowWatcher.class);
ArrayList<String> methods = new ArrayList<>();
for (Class c : classes) {
for (Method method : c.getMethods()) {
if (method.getParameterTypes().length != 1) {
continue;
} else if (method.getName().startsWith("get")) {
continue;
} else if (method.isAnnotationPresent(Deprecated.class) &&
!method.isAnnotationPresent(NmsRemovedIn.class)) {
continue;
} else if (!method.getReturnType().equals(Void.TYPE)) {
continue;
} else if (method.getName().equals("removePotionEffect")) {
continue;
} else if (!FlagWatcher.class.isAssignableFrom(method.getDeclaringClass())) {
continue;
} else if (LibsPremium.isPremium() && new Random().nextBoolean()) {
continue;
}
int added = -1;
int removed = -1;
if (method.isAnnotationPresent(NmsAddedIn.class)) {
added = method.getAnnotation(NmsAddedIn.class).val().ordinal();
} else if (method.getDeclaringClass().isAnnotationPresent(NmsAddedIn.class)) {
added = method.getDeclaringClass().getAnnotation(NmsAddedIn.class).val().ordinal();
}
if (method.isAnnotationPresent(NmsRemovedIn.class)) {
removed = method.getAnnotation(NmsRemovedIn.class).val().ordinal();
} else if (method.getDeclaringClass().isAnnotationPresent(NmsRemovedIn.class)) {
removed = method.getDeclaringClass().getAnnotation(NmsRemovedIn.class).val().ordinal();
}
Class<?> param = method.getParameterTypes()[0];
String s = ((added >= 0 || removed >= 0) ? added + ":" + removed + ":" : "") +
method.getDeclaringClass().getSimpleName() + ":" + method.getName() + ":" + param.getName();
if (methods.contains(s)) {
continue;
}
methods.add(s);
}
}
File methodsFile = new File("target/classes/ANTI_PIRACY_ENCRYPTION");
try (PrintWriter writer = new PrintWriter(methodsFile, "UTF-8")) {
writer.write(StringUtils.join(methods, "\n"));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}

View File

@@ -1,133 +0,0 @@
package me.libraryaddict.disguise.utilities.reflection;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.FlagWatcher;
import org.apache.commons.lang.ClassUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by libraryaddict on 13/02/2020.
*/
public class DisguiseMethods {
private HashMap<Class<? extends FlagWatcher>, List<Method>> watcherMethods = new HashMap<>();
public ArrayList<Method> getMethods(Class c) {
ArrayList<Method> methods = new ArrayList<>();
if (watcherMethods.containsKey(c)) {
methods.addAll(watcherMethods.get(c));
}
if (c != FlagWatcher.class) {
methods.addAll(getMethods(c.getSuperclass()));
}
return methods;
}
public DisguiseMethods() {
try (InputStream stream = LibsDisguises.getInstance().getResource("ANTI_PIRACY_ENCRYPTION")) {
List<String> lines = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.toList());
HashMap<String, Class<? extends FlagWatcher>> classes = new HashMap<>();
classes.put(FlagWatcher.class.getSimpleName(), FlagWatcher.class);
for (DisguiseType t : DisguiseType.values()) {
if (t.getWatcherClass() == null) {
continue;
}
Class c = t.getWatcherClass();
while (!classes.containsKey(c.getSimpleName())) {
classes.put(c.getSimpleName(), c);
c = ReflectionManager.getSuperClass(c);
}
}
for (String line : lines) {
String[] split = line.split(":");
if (split.length > 3) {
int added = Integer.parseInt(split[0]);
int removed = Integer.parseInt(split[1]);
if (added >= 0 && added > ReflectionManager.getVersion().ordinal()) {
continue;
} else if (removed >= 0 && removed <= ReflectionManager.getVersion().ordinal()) {
continue;
}
}
Class<? extends FlagWatcher> watcher = classes.get(split[split.length - 3]);
if (watcher == null) {
continue;
}
String paramName = split[split.length - 1];
Class param;
if (!paramName.contains(".")) {
param = parseType(paramName);
} else {
param = Class.forName(paramName);
}
Method method = watcher.getMethod(split[split.length - 2], param);
watcherMethods.computeIfAbsent(watcher, (a) -> new ArrayList<>()).add(method);
}
}
catch (IOException | ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* Return the java {@link java.lang.Class} object with the specified class name.
* <p>
* This is an "extended" {@link java.lang.Class#forName(java.lang.String) } operation.
* <p>
* + It is able to return Class objects for primitive types
* + Classes in name space `java.lang` do not need the fully qualified name
* + It does not throw a checked Exception
*
* @param className The class name, never `null`
* @throws IllegalArgumentException if no class can be loaded
*/
private Class<?> parseType(final String className) {
switch (className) {
case "boolean":
return boolean.class;
case "byte":
return byte.class;
case "short":
return short.class;
case "int":
return int.class;
case "long":
return long.class;
case "float":
return float.class;
case "double":
return double.class;
case "char":
return char.class;
case "[I":
return int[].class;
default:
throw new IllegalArgumentException("Class not found: " + className);
}
}
}

View File

@@ -1,54 +0,0 @@
package me.libraryaddict.disguise.utilities.reflection;
import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import java.util.HashMap;
public class DisguiseValues {
private static HashMap<DisguiseType, DisguiseValues> values = new HashMap<>();
public static DisguiseValues getDisguiseValues(DisguiseType type) {
return values.get(type);
}
public static Class getNmsEntityClass(DisguiseType type) {
return getDisguiseValues(type).getNmsEntityClass();
}
private FakeBoundingBox adultBox;
private FakeBoundingBox babyBox;
private float[] entitySize;
private double maxHealth;
private Class nmsEntityClass;
public DisguiseValues(DisguiseType type, Class classType, int entitySize, double maxHealth) {
values.put(type, this);
nmsEntityClass = classType;
this.maxHealth = maxHealth;
}
public FakeBoundingBox getAdultBox() {
return adultBox;
}
public FakeBoundingBox getBabyBox() {
return babyBox;
}
public double getMaxHealth() {
return maxHealth;
}
public Class getNmsEntityClass() {
return nmsEntityClass;
}
public void setAdultBox(FakeBoundingBox newBox) {
adultBox = newBox;
}
public void setBabyBox(FakeBoundingBox newBox) {
babyBox = newBox;
}
}

View File

@@ -10,15 +10,12 @@ import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.disguisetypes.*;
import me.libraryaddict.disguise.disguisetypes.watchers.*;
import me.libraryaddict.disguise.utilities.DisguiseSound;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.LibsPremium;
import me.libraryaddict.disguise.utilities.*;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.*;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.bukkit.entity.*;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
@@ -1013,7 +1010,7 @@ public class ReflectionManager {
Object obj = ((WrappedChatComponent) val).getHandle();
return NmsVersion.v1_13.isSupported() ? Optional.of(obj) : com.google.common.base.Optional.of(obj);
}else if (!NmsVersion.v1_13.isSupported()) {
} else if (!NmsVersion.v1_13.isSupported()) {
return com.google.common.base.Optional.of(val);
}
} else if (value instanceof Vector3F) {
@@ -1063,6 +1060,71 @@ public class ReflectionManager {
return value;
}
public static Material getMaterial(String name) {
try {
if (!NmsVersion.v1_13.isSupported()) {
Method toMinecraft = getCraftMethod("util.CraftMagicNumbers", "getMaterialFromInternalName",
String.class);
return (Material) toMinecraft.invoke(null, name);
}
Object mcKey = getNmsConstructor("MinecraftKey", String.class).newInstance(name);
Object registry = getNmsField("IRegistry", "ITEM").get(null);
Method getMethod = getNmsMethod(getNmsClass("RegistryMaterials"), "get", mcKey.getClass());
Object item = getMethod.invoke(registry, mcKey);
if (item == null) {
return null;
}
Method getMaterial = getCraftMethod("util.CraftMagicNumbers", "getMaterial", getNmsClass("Item"));
return (Material) getMaterial.invoke(null, item);
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String getItemName(Material material) {
try {
Object item = getCraftMethod("util.CraftMagicNumbers", "getItem", Material.class).invoke(null, material);
if (item == null) {
return null;
}
Object registry;
if (NmsVersion.v1_13.isSupported()) {
registry = getNmsField("IRegistry", "ITEM").get(null);
} else {
registry = getNmsField("Item", "REGISTRY").get(null);
}
Method getMethod = getNmsMethod(registry.getClass(), NmsVersion.v1_13.isSupported() ? "getKey" : "b",
Object.class);
Object mcKey = getMethod.invoke(registry, item);
if (mcKey == null) {
return null;
}
return (String) getNmsMethod("MinecraftKey", "getKey").invoke(mcKey);
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static Object getNmsVillagerData(VillagerData data) {
Object type = getVillagerType(data.getType());
Object profession = getVillagerProfession(data.getProfession());

View File

@@ -0,0 +1,61 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import com.google.gson.Gson;
import org.objectweb.asm.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
/**
* Created by libraryaddict on 17/02/2020.
*/
public class Asm13 implements IAsm {
public Class<?> createClassWithoutMethods(String className,
ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException,
IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
ClassReader cr = new ClassReader(
getClass().getClassLoader().getResourceAsStream(className.replace(".", "/") + ".class"));
ClassWriter writer = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM5, writer) {
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
Map.Entry<String, String> entry = illegalMethods.stream()
.filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null);
if (entry != null) {
return null;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
byte[] bytes = writer.toByteArray();
ClassLoader loader = getClass().getClassLoader();
Field field = loader.getClass().getDeclaredField("classes");
field.setAccessible(true);
Map<String, Class<?>> map = (Map<String, Class<?>>) field.get(loader);
Class newClass =
(Class<?>) getDefineClassMethod()
.invoke(getClass().getClassLoader(), className, bytes, 0, bytes.length);
map.put(className, newClass);
return newClass;
}
private static Method getDefineClassMethod() throws NoSuchMethodException {
Method defineClass = ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
return defineClass;
}
}

View File

@@ -0,0 +1,60 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import org.objectweb.asm.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
/**
* Created by libraryaddict on 17/02/2020.
*/
public class Asm14 implements IAsm {
public Class<?> createClassWithoutMethods(String className,
ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException,
IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
ClassReader cr = new ClassReader(
getClass().getClassLoader().getResourceAsStream(className.replace(".", "/") + ".class"));
ClassWriter writer = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM5, writer) {
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
Map.Entry<String, String> entry = illegalMethods.stream()
.filter(e -> e.getKey().equals(name) && e.getValue().equals(desc)).findFirst().orElse(null);
if (entry != null) {
return null;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
byte[] bytes = writer.toByteArray();
ClassLoader loader = getClass().getClassLoader();
Field field = loader.getClass().getDeclaredField("classes");
field.setAccessible(true);
Map<String, Class<?>> map = (Map<String, Class<?>>) field.get(loader);
Class newClass =
(Class<?>) getDefineClassMethod()
.invoke(getClass().getClassLoader(), className, bytes, 0, bytes.length);
map.put(className, newClass);
return newClass;
}
private static Method getDefineClassMethod() throws NoSuchMethodException {
Method defineClass = ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
return defineClass;
}
}

View File

@@ -0,0 +1,15 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Map;
/**
* Created by libraryaddict on 17/02/2020.
*/
public interface IAsm {
public Class<?> createClassWithoutMethods(String className, ArrayList<Map.Entry<String, String>> illegalMethods) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException;
}

View File

@@ -0,0 +1,41 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import lombok.Getter;
import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import java.lang.reflect.Type;
/**
* Created by libraryaddict on 17/02/2020.
*/
@Getter
public class WatcherInfo {
private int added = -1;
private int removed = -1;
private String watcher;
private String method;
private String param;
String descriptor;
public WatcherInfo(String string) {
String[] split = string.split(":", -1);
if (split.length > 3) {
descriptor = split[3];
added = Integer.parseInt(split[4]);
removed = Integer.parseInt(split[5]);
}
watcher = split[0];
method = split[1];
param = split[2];
}
public boolean isSupported() {
if (getAdded() >= 0 && added > ReflectionManager.getVersion().ordinal()) {
return false;
}
return getRemoved() < 0 || removed > ReflectionManager.getVersion().ordinal();
}
}

View File

@@ -0,0 +1,60 @@
package me.libraryaddict.disguise.utilities.reflection.asm;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.reflection.NmsVersion;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by libraryaddict on 17/02/2020.
*/
public class WatcherSanitizer {
public static void init() {
IAsm asm;
if (NmsVersion.v1_14.isSupported()) {
asm = new Asm14();
} else {
asm = new Asm13();
}
try (InputStream stream = LibsDisguises.getInstance().getResource("ANTI_PIRACY_ENCRYPTION")) {
List<String> lines = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.toList());
LinkedHashMap<String, ArrayList<Map.Entry<String, String>>> toRemove = new LinkedHashMap<>();
for (String s : lines) {
WatcherInfo info = new WatcherInfo(s);
if (info.isSupported()) {
continue;
}
String path = "me.libraryaddict.disguise.disguisetypes." +
(info.getWatcher().equals("FlagWatcher") ? "" : "watchers.") + info.getWatcher();
toRemove.putIfAbsent(path, new ArrayList<>());
ArrayList<Map.Entry<String, String>> list = toRemove.get(path);
list.add(new HashMap.SimpleEntry(info.getMethod(), info.getDescriptor()));
}
for (Map.Entry<String, ArrayList<Map.Entry<String, String>>> entry : toRemove.entrySet()) {
Class result = asm.createClassWithoutMethods(entry.getKey(), entry.getValue());
}
}
catch (IOException | NoClassDefFoundError | IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}