Skip to content

Instantly share code, notes, and snippets.

@Unh0lyTigg
Created March 7, 2016 12:27
Show Gist options
  • Save Unh0lyTigg/47919b4afa2d16ef259b to your computer and use it in GitHub Desktop.
Save Unh0lyTigg/47919b4afa2d16ef259b to your computer and use it in GitHub Desktop.
package xyz.unh0lytigg.jsoncrops.transformer.impl;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import xyz.unh0lytigg.jsoncrops.transformer.*;
import xyz.unh0lytigg.jsoncrops.transformer.util.*;
public class GuiMainMenuTransformer implements Opcodes, ITransformClass {
@Override
public String getTargetClass() {
return "net.minecraft.client.gui.GuiMainMenu";
}
@Override
public byte[] transform(byte[] basicClass) {
ClassNode node = createNode(basicClass);
boolean change = false;
for (MethodNode method : node.methods) {
if ("drawScreen".equals(method.name) && "(IIF)V".equals(method.desc)) {
InsnList instructions = method.instructions;
ListIterator<AbstractInsnNode> iter = instructions.iterator();
InstructionListHelper helper = new InstructionListHelper(iter);
AtomicReference<AbstractInsnNode> needle = new AtomicReference<>();
AtomicBoolean ldc = new AtomicBoolean();
helper.debug().with(InstructionListHelper.ldcFind("Copyright Mojang AB. Do not distribute!", ldc), (n -> {needle.set(n); return true;}), LdcInsnNode.class).run();
}
}
return change ? writeNode(node) : basicClass;
}
}
package xyz.unh0lytigg.jsoncrops.transformer.util;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.stream.*;
import org.objectweb.asm.*;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import com.google.common.collect.*;
import lombok.*;
@RequiredArgsConstructor
public final class InstructionListHelper implements Opcodes {
private static class NodeFunc {
public Predicate<AbstractInsnNode> predicate;
public Function<AbstractInsnNode, Boolean> function;
}
private static Field fLabelNode_label = null;
static {
try {
fLabelNode_label = LabelNode.class.getDeclaredField("label");
fLabelNode_label.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
}
@NonNull
private final ListIterator<AbstractInsnNode> iter;
private List<NodeFunc> mappingFuncs = Lists.newArrayList();
private boolean debugAdded = false;
public InstructionListHelper debug() {
if (debugAdded) return this;
debugAdded = true;
return with(InstructionListHelper::debug_predicate, InstructionListHelper::debug_func, AbstractInsnNode.class);
}
private static boolean debug_predicate(AbstractInsnNode node) {
if (!(node instanceof LineNumberNode))
System.out.println("\t"+node.getClass().getSimpleName() + ": " + node.getOpcode() + " (" + OPCODE_MAP.get(Integer.valueOf(node.getOpcode())) + ")");
if (node instanceof LineNumberNode) {
LineNumberNode lnn = (LineNumberNode)node;
LabelNode l = lnn.start;
Label label = null;
try {
label = (Label) fLabelNode_label.get(l);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(String.format("\tLineNumberNode: %d (%s): %d / %s", lnn.getOpcode(), OPCODE_MAP.get(Integer.valueOf(lnn.getOpcode())), lnn.line, label == null ? "[unknown_label]" : String.valueOf(label.info)));
}
if ((node instanceof LineNumberNode) || (node instanceof FrameNode))
return false;
return true;
}
private static String transformName(String raw) {
return raw == null ? "" : raw.replaceAll("[/\\\\\\Q$\\E]", ".");
}
private static boolean debug_func(AbstractInsnNode node) {
if (node instanceof VarInsnNode) {
VarInsnNode v = (VarInsnNode)node;
if (v.getOpcode() == ALOAD && v.var == 0)
System.out.println("\t\tthis");
else
System.out.println("\t\t" + v.var);
} else if (node instanceof MethodInsnNode) {
MethodInsnNode m = (MethodInsnNode)node;
System.out.println("\t\t" + transformName(m.owner) + "." + m.name + m.desc);
} else if (node instanceof FieldInsnNode) {
FieldInsnNode f = (FieldInsnNode)node;
System.out.println("\t\t" + f.desc + " " + transformName(f.owner) + "." + f.name);
} else if (node instanceof InsnNode) {
String msg = null;
switch (node.getOpcode()) {
case ICONST_0:
msg = "\tConstant 0"; break;
case ICONST_1:
msg = "\tConstant 1"; break;
case ICONST_2:
msg = "\tConstant 2"; break;
case ICONST_3:
msg = "\tConstant 3"; break;
case ICONST_4:
msg = "\tConstant 4"; break;
case ICONST_5:
msg = "\tConstant 5"; break;
case ICONST_M1:
msg = "\tConstant -1"; break;
case DUP:
msg = "\tDuplicate"; break;
case LCONST_0:
msg = "\tConstant 0L"; break;
case LCONST_1:
msg = "\tConstant 1L"; break;
case AASTORE:
msg = "\tStore to array"; break;
}
if (msg != null) System.out.println("\t"+msg);
} else if (node instanceof TypeInsnNode) {
TypeInsnNode t = (TypeInsnNode)node;
System.out.println("\t\t" + t.desc);
} else if (node instanceof LdcInsnNode) {
LdcInsnNode ldc = (LdcInsnNode)node;
if (ldc.cst instanceof String)
System.out.println("\t\t" + ldc.cst.getClass().getName() + " \"" + ldc.cst + "\"");
else
System.out.println("\t\t" + ldc.cst.getClass().getName() + " " + ldc.cst);
} else if (node instanceof LabelNode) {
LabelNode l = (LabelNode)node;
Label label = null;
try {
label = (Label) fLabelNode_label.get(l);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
if (label != null) {
if (label.info instanceof String)
System.out.println("\t\t Label: \"" + label.info + "\"");
else
System.out.println("\t\t Label: " + label.info);
}
} else if (node instanceof JumpInsnNode) {
JumpInsnNode j = (JumpInsnNode)node;
LabelNode l = j.label;
Label label = null;
try {
label = (Label) fLabelNode_label.get(l);
System.out.println("\t\t\tlabel is " + label);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
String msg = null;
switch (node.getOpcode()) {
case IF_ACMPNE:
msg = "\tRef a != b ? branch"; break;
case IF_ACMPEQ:
msg = "\tRef a == b ? branch"; break;
case IF_ICMPEQ:
msg = "\tInt a == b ? branch"; break;
case IF_ICMPGE:
msg = "\tInt a >= b ? branch"; break;
case IF_ICMPGT:
msg = "\tInt a > b ? branch"; break;
case IF_ICMPLE:
msg = "\tInt a <= b ? branch"; break;
case IF_ICMPLT:
msg = "\tInt a < b ? branch"; break;
case IF_ICMPNE:
msg = "\tInt a != b ? branch"; break;
case IFEQ:
msg = "\tVal a == b ? branch"; break;
case IFGE:
msg = "\tVal a >= b ? branch"; break;
case IFGT:
msg = "\tVal a > b ? branch"; break;
case IFLE:
msg = "\tVal a <= b ? branch"; break;
case IFLT:
msg = "\tVal a < b ? branch"; break;
case IFNE:
msg = "\tVal a != b ? branch"; break;
case IFNONNULL:
msg = "\tVal a != null ? branch"; break;
case IFNULL:
msg = "\tVal a == null ? branch"; break;
}
if (msg != null && label != null)
msg = msg + " to " + label.info;
if (msg != null) System.out.println("\t"+msg);
} else if (node instanceof IntInsnNode) {
IntInsnNode i = (IntInsnNode)node;
switch (node.getOpcode()) {
case SIPUSH:
System.out.println("\t\tPush Short " + i.operand); break;
case NEWARRAY:
System.out.println("\t\tNew Array " + i.operand); break;
case BIPUSH:
System.out.println("\t\tBipush " + i.operand); break;
}
} else {
System.out.println("\t\tUnknown node class: " + node.getClass().getSimpleName() + " (op:" + OPCODE_MAP.get(Integer.valueOf(node.getOpcode())) + ")");
}
return false;
}
private static Map<Integer, String> OPCODE_MAP = Maps.newHashMap();
static {
Stream.of(Opcodes.class.getFields()).filter(f -> f.getType().isPrimitive()).forEach(f -> {
try {
int i = f.getInt(null);
OPCODE_MAP.put(Integer.valueOf(i), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
});
}
@SuppressWarnings("unchecked")
public <T extends AbstractInsnNode> InstructionListHelper with(Predicate<T> p, Function<T, Boolean> f, Class<T> clazz) {
Function<AbstractInsnNode, Boolean> f_generic = n -> f.apply((T)n);
Predicate<AbstractInsnNode> p_generic = n -> {
if (clazz.isAssignableFrom(n.getClass())) return p.test((T)n);
return false;
};
NodeFunc nf = new NodeFunc();
nf.predicate = p_generic;
nf.function = f_generic;
mappingFuncs.add(nf);
return this;
}
public InstructionListHelper clear() {
mappingFuncs.clear();
debugAdded = false;
return this;
}
public boolean run() {
boolean state = false;
iterloop:
while (iter.hasNext()) {
AbstractInsnNode node = iter.next();
for (NodeFunc e : mappingFuncs) {
if (e.predicate.test(node))
if (e.function.apply(node)) {
state = true;
break iterloop;
}
}
}
return state;
}
public static Predicate<AbstractInsnNode> opcodeCheck(int op) {
return n -> n.getOpcode() == op;
}
public static Predicate<AbstractInsnNode> methodName(String name) {
return n -> {
if(n instanceof MethodInsnNode)
return ((MethodInsnNode)n).name.equals(name);
return false;
};
}
public static Predicate<AbstractInsnNode> atomicBoolean(AtomicBoolean b) {
return n -> b.get();
}
public static <T extends AbstractInsnNode> Predicate<AbstractInsnNode> instanceCheck(Class<T> c) {
return n -> c.isAssignableFrom(n.getClass());
}
public static Predicate<AbstractInsnNode> ownerExists(Predicate<String> ref) {
return n -> {
if (n instanceof FieldInsnNode)
return ref.test(((FieldInsnNode)n).owner);
else if (n instanceof MethodInsnNode)
return ref.test(((MethodInsnNode)n).owner);
return false;
};
}
public static Function<LdcInsnNode, Boolean> ldcFind(String cst, AtomicBoolean b) {
return n -> {
if (n.cst instanceof String && n.cst.equals(cst))
b.set(true);
return false;
};
}
public static Function<LdcInsnNode, Boolean> ldcFind(float cst, AtomicBoolean b) {
return n -> {
if (n.cst instanceof Float && n.cst.equals(cst))
b.set(true);
return false;
};
}
public static Function<LdcInsnNode, Boolean> ldcFind(int cst, AtomicBoolean b) {
return n -> {
if (n.cst instanceof Integer && n.cst.equals(cst))
b.set(true);
return false;
};
}
public static Function<LdcInsnNode, Boolean> ldcFind(long cst, AtomicBoolean b) {
return n -> {
if (n.cst instanceof Long && n.cst.equals(cst))
b.set(true);
return false;
};
}
public static Function<LdcInsnNode, Boolean> ldcFind(Type cst, AtomicBoolean b) {
return n -> {
if (n.cst instanceof Type && n.cst.equals(cst))
b.set(true);
return false;
};
}
public static Function<AbstractInsnNode, Boolean> mapOwner(Function<String, Boolean> consumer) {
return n -> {
if (n instanceof MethodInsnNode)
return consumer.apply(((MethodInsnNode)n).owner);
else if (n instanceof FieldInsnNode)
return consumer.apply(((FieldInsnNode)n).owner);
return false;
};
}
}
package xyz.unh0lytigg.jsoncrops.transformer;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
public interface ITransformClass {
public String getTargetClass();
public byte[] transform(byte[] basicClass);
public default ClassNode createNode(byte[] classData) {
ClassNode node = new ClassNode();
ClassReader reader = new ClassReader(classData);
reader.accept(node, 0);
return node;
}
public default byte[] writeNode(ClassNode node) {
ClassWriter writer = new ClassWriter(Opcodes.ASM5);
node.accept(writer);
return writer.toByteArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment