Skip to content

Instantly share code, notes, and snippets.

@FormallyMyles
Created August 13, 2016 20:32
Show Gist options
  • Save FormallyMyles/e9d23ebaebcbfc3ebebd1d88fdf15ec4 to your computer and use it in GitHub Desktop.
Save FormallyMyles/e9d23ebaebcbfc3ebebd1d88fdf15ec4 to your computer and use it in GitHub Desktop.
DatawatcherCollector - Compile then run passing in jar file
package us.myles.dwc;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class DatawatcherCollector {
private static String entity;
private static String dataWatcher;
private static String entityTypes;
private static Map<String, ClassNode> classes = new HashMap<>();
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
System.out.println("Collecting useful information...");
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("/"))) {
ClassReader reader = new ClassReader(jarFile.getInputStream(entry));
ClassNode node = new ClassNode();
reader.accept(node, ClassReader.EXPAND_FRAMES);
classes.put(entry.getName().replace('/', '.').replace(".class", ""), node);
}
}
System.out.println("Finding required classes...");
System.out.println();
// Using magic technology find classes :D
entity = findClassFromConstant("entityBaseTick");
entityTypes = findClassFromConstant("Skipping Entity with id {}");
dataWatcher = findClassFromConstant("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);
System.out.println("EntityTypes Class: " + entityTypes);
System.out.println("DataWatcher Class: " + dataWatcher);
System.out.println();
System.out.println("Attempting to Map Parents...");
List<String> queue = new ArrayList<>(classes.keySet());
ClassTree object = new ClassTree("java.lang.Object");
String current = null;
int total = queue.size();
while (queue.size() > 0) {
if (queue.size() % 100 == 0) {
System.out.println((total - queue.size()) + "/" + total + " done...");
}
if (object.contains(current) || current == null) {
current = queue.get(0);
}
ClassNode clazz = classes.get(current);
if (clazz != null) {
// Check super
String superC = clazz.superName.replace('/', '.');
if (object.getName().equals(superC) || object.contains(superC)) {
object.insert(superC, current);
queue.remove(current);
current = null;
} else {
if (queue.contains(superC)) {
current = superC;
} else {
queue.remove(current);
current = null;
}
}
} else {
queue.remove(current);
current = null;
}
}
System.out.println("Fetching entity tree...");
System.out.println();
ClassTree tree = object.find(entity); // Name resolver?
print(tree, 0);
System.out.println();
System.out.println("Printing Metadata:");
metadataTree(null, tree, 0);
}
public static String resolveName(String clazz) {
if (clazz.equals(entity)) return "Entity";
ClassNode entityTypesNode = classes.get(entityTypes);
InvokeClassStringExtractor extractor = new InvokeClassStringExtractor(clazz, entityTypes);
entityTypesNode.accept(extractor);
if (extractor.getFoundName() == null)
return "Unknown(" + clazz + ")";
return extractor.getFoundName();
}
public static void metadataTree(ClassTree parent, ClassTree tree, int i) {
List<String> mt = metadata(tree.getName());
System.out.println();
System.out.println(tree.getName() + "(" + resolveName(tree.getName()) + ")" + " Parent: " + (parent == null ? "None" : resolveName(parent.getName())));
// print each metadata for this class
for (String meta : mt) {
System.out.println(i++ + ". " + meta);
}
// print children next
for (ClassTree item : tree.getChildren()) {
metadataTree(tree, item, i);
}
}
private static List<String> metadata(String name) {
List<String> results = new ArrayList<>();
ClassNode node = classes.get(name);
List<MethodNode> methods = node.methods;
for (MethodNode method : methods) {
if (method.name.equals("<clinit>")) {
// Static init
MethodInsnNode lastMethod = null;
for (AbstractInsnNode insn : method.instructions.toArray()) {
if (insn instanceof MethodInsnNode) {
if (((MethodInsnNode) insn).owner.equals(dataWatcher)) {
lastMethod = (MethodInsnNode) insn;
}
}
if (insn instanceof FieldInsnNode) {
FieldInsnNode fieldInsn = (FieldInsnNode) insn;
if (insn.getOpcode() == Opcodes.PUTSTATIC) {
if (lastMethod != null) {
if (!fieldInsn.owner.equals(name)) {
continue;
}
// Find field
for (FieldNode fieldNode : (List<FieldNode>) node.fields) {
if (fieldNode.name.equals(fieldInsn.name)) {
// Got signature
results.add("DataWatcher." + lastMethod.name + "() - " + fieldNode.name + " - " + fieldNode.signature);
break;
}
}
}
}
lastMethod = null;
}
}
}
}
return results;
}
public static void print(ClassTree tree, int depth) {
System.out.println(new String(new char[depth]).replace("\0", "-") + "> " + tree.getName() + "(" + resolveName(tree.getName()) + ")");
for (ClassTree item : tree.getChildren()) {
print(item, depth + 1);
}
}
public static String findClassFromConstant(String str) {
for (Map.Entry<String, ClassNode> s : classes.entrySet()) {
ClassNode clazz = s.getValue();
List<MethodNode> methods = clazz.methods;
for (MethodNode method : methods) {
for (AbstractInsnNode insnNode : method.instructions.toArray()) {
if (insnNode instanceof LdcInsnNode) {
LdcInsnNode ldc = (LdcInsnNode) insnNode;
if (str.equals(ldc.cst)) {
return s.getKey();
}
}
}
}
}
return null;
}
}
class InvokeClassStringExtractor extends ClassVisitor {
private final String entityTypes;
private final String classToFind;
public String foundName;
public InvokeClassStringExtractor(String classToFind, String entityTypes) {
super(Opcodes.ASM5);
this.entityTypes = entityTypes;
this.classToFind = classToFind;
}
public String getFoundName() {
return foundName;
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) {
if (Modifier.isStatic(access) && foundName == null) {
return new MethodVisitor(Opcodes.ASM4, super.visitMethod(access, methodName, desc, signature, exceptions)) {
private Stack args = new Stack();
@Override
public void visitLdcInsn(Object cst) {
if (foundName != null) return;
args.push(cst);
super.visitLdcInsn(cst);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (foundName != null) return;
if (owner.equals(entityTypes)) {
// Take the last variables on the stack
Type[] argTypes = Type.getArgumentTypes(desc);
boolean clazz = false;
boolean string = false;
for (Type t : argTypes) {
if (t.getClassName().equals("java.lang.Class")) {
clazz = true;
}
if (t.getClassName().equals("java.lang.String")) {
string = true;
}
}
if (clazz && string) {
// Get last string
String className = null;
String entityName = null;
for (int i = 0; i < argTypes.length; i++) {
if (args.size() > 0) {
Object pop = args.pop();
if (pop instanceof Type) {
if (!((Type) pop).getClassName().equals(classToFind)) {
break;
} else {
if (entityName != null) {
foundName = entityName;
return;
} else {
className = ((Type) pop).getClassName();
}
}
}
if (pop instanceof String) {
if (className != null) {
foundName = (String) pop;
return;
} else {
entityName = (String) pop;
}
}
}
}
}
}
// Clear otherwise
args.clear();
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
};
} else {
return super.visitMethod(access, methodName, desc, signature, exceptions);
}
}
}
class ClassTree {
private String name;
private ArrayList<ClassTree> children = new ArrayList<>();
public ClassTree(String name) {
this.name = name;
}
public String getName() {
return name;
}
public ArrayList<ClassTree> getChildren() {
return children;
}
public boolean contains(String name) {
for (ClassTree item : children) {
if (item.getName().equals(name)) return true;
if (item.contains(name)) return true;
}
return false;
}
public ClassTree find(String name) {
for (ClassTree item : children) {
if (item.getName().equals(name)) return item;
if (item.contains(name)) return item.find(name);
}
return null;
}
public boolean insert(String superclass, String name) {
if (getName().equals(superclass)) {
children.add(new ClassTree(name));
return true;
} else {
for (ClassTree item : children) {
if (item.insert(superclass, name))
return true;
}
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment