Created
August 9, 2016 22:46
-
-
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.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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