Skip to content

Instantly share code, notes, and snippets.

@FormallyMyles
Created August 9, 2016 22:46
Show Gist options
  • Save FormallyMyles/cf2b49de895cfe9fe024d79f56a98556 to your computer and use it in GitHub Desktop.
Save FormallyMyles/cf2b49de895cfe9fe024d79f56a98556 to your computer and use it in GitHub Desktop.
DataWatcherCollector for Minecraft, (Strips DataWatcher data from a Server jar, based on constants.)
package us.myles.dwc;
import javassist.*;
import javassist.bytecode.SignatureAttribute;
import javassist.expr.ExprEditor;
import javassist.expr.FieldAccess;
import javassist.expr.MethodCall;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class Main {
private static CtClass entity;
private static CtClass dataWatcher;
private static CtClass entityTypes;
public static void main(String[] args) throws Exception {
JarFile jarFile = new JarFile(args[0]);
// Entity - entityBaseTick
// EntityTypes - Skipping Entity with id {}
// DataWatcher - Data value id is too big with
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(jarFile.getName());
System.out.println("Collecting useful information...");
List<String> classes = new ArrayList<String>();
Enumeration<JarEntry> iter = jarFile.entries();
while (iter.hasMoreElements()) {
JarEntry entry = iter.nextElement();
if (entry.getName().endsWith(".class") && (entry.getName().startsWith("net/minecraft") || !entry.getName().contains("/"))) {
classes.add(entry.getName().replace('/', '.').replace(".class", ""));
}
}
System.out.println("Finding required classes...");
System.out.println();
// Using magic technology find classes :D
entity = findClassFromConstant(pool, classes, "entityBaseTick");
entityTypes = findClassFromConstant(pool, classes, "Skipping Entity with id {}");
dataWatcher = findClassFromConstant(pool, classes, "Data value id is too big with ");
if (entity == null || entityTypes == null || dataWatcher == null) {
throw new RuntimeException("Constants have changed!! ");
}
System.out.println("Entity Class: " + entity.getName());
System.out.println("EntityTypes Class: " + entityTypes.getName());
System.out.println("DataWatcher Class: " + dataWatcher.getName());
System.out.println();
System.out.println("Attempting to Map Parents...");
ClassTree object = new ClassTree("java.lang.Object");
String current = null;
int total = classes.size();
while (classes.size() > 0) {
if (classes.size() % 100 == 0) {
System.out.println((total - classes.size()) + "/" + total + " done...");
}
if (object.contains(current) || current == null) {
current = classes.get(0);
}
CtClass clazz = pool.getOrNull(current);
if (clazz != null) {
// Check super
String superC = clazz.getClassFile().getSuperclass();
if (object.getName().equals(superC) || object.contains(superC)) {
object.insert(superC, clazz.getName());
classes.remove(clazz.getName());
current = null;
} else {
if (classes.contains(superC)) {
current = superC;
} else {
classes.remove(current);
current = null;
}
}
} else {
classes.remove(current);
current = null;
}
}
System.out.println("Fetching entity tree...");
System.out.println();
ClassTree tree = object.find(entity.getName()); // Name resolver?
print(pool, tree, 0);
System.out.println();
System.out.println("Printing Metadata:");
metadataTree(pool, tree, 0);
}
public static String resolveName(ClassPool pool, CtClass clazz) {
if (clazz.equals(entity)) return "Entity";
boolean name = false;
int distance = 0;
for (int i = 0; i < entityTypes.getClassFile().getConstPool().getSize(); i++) {
try {
if (name) {
if(distance > 3) return "Unknown(" + clazz.getName() + ")";
return entityTypes.getClassFile().getConstPool().getStringInfo(i);
} else {
if (entityTypes.getClassFile().getConstPool().getClassInfo(i).equals(clazz.getName())) {
name = true;
}
}
} catch (Exception e) {
if (name)
distance++;
}
}
return "Unknown(" + clazz.getName() + ")";
}
public static void metadataTree(ClassPool pool, ClassTree tree, int i) {
List<String> mt = metadata(pool, tree.getName());
System.out.println();
try {
System.out.println(tree.getName() + "(" + resolveName(pool, pool.getOrNull(tree.getName())) + ")" + (i > 0 ? " Parent: " + resolveName(pool, pool.getOrNull(tree.getName()).getSuperclass()) : "")); // todo name resolver
} catch (NotFoundException e) {
e.printStackTrace();
}
// print each metadata for this class
for (String meta : mt) {
System.out.println(i++ + ". " + meta);
}
// print children next
for (ClassTree item : tree.getChildren()) {
metadataTree(pool, item, i);
}
}
private static List<String> metadata(ClassPool pool, String name) {
List<String> results = new ArrayList<>();
CtMethod[] lastMethod = new CtMethod[1];
CtClass currentEntity = pool.getOrNull(name);
if (currentEntity == null) return results;
try {
if (currentEntity.getClassInitializer() == null) return results; // :(
currentEntity.getClassInitializer().instrument(new ExprEditor() {
@Override
public void edit(FieldAccess f) throws CannotCompileException {
try {
if (!f.getField().getDeclaringClass().equals(currentEntity)) return;
CtField field = f.getField();
if (lastMethod[0] != null) {
results.add("DataWatcher." + lastMethod[0].getName() + "() - " + field.getName() + " - " + field.getGenericSignature());
lastMethod[0] = null;
}
} catch (NotFoundException e) {
e.printStackTrace();
}
}
@Override
public void edit(MethodCall m) throws CannotCompileException {
try {
if (m.getMethod().getDeclaringClass().equals(dataWatcher)) {
lastMethod[0] = m.getMethod();
}
} catch (NotFoundException e) {
e.printStackTrace();
}
}
});
} catch (CannotCompileException e) {
e.printStackTrace();
}
return results;
}
public static void print(ClassPool pool, ClassTree tree, int depth) {
System.out.println(new String(new char[depth]).replace("\0", "-") + "> " + tree.getName() + "(" + resolveName(pool, pool.getOrNull(tree.getName())) + ")");
for (ClassTree item : tree.getChildren()) {
print(pool, item, depth + 1);
}
}
public static CtClass findClassFromConstant(ClassPool pool, List<String> classes, String str) {
for (String s : classes) {
CtClass clazz = pool.getOrNull(s);
if (clazz != null) {
for (int i = 0; i < clazz.getClassFile().getConstPool().getSize(); i++) {
if (str.equals(clazz.getClassFile().getConstPool().getLdcValue(i))) {
return clazz;
}
}
}
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment