2013-07-16 17:58:02 +10:00
|
|
|
package codechicken.lib.asm;
|
|
|
|
|
|
2014-04-09 13:38:05 +10:00
|
|
|
import codechicken.lib.asm.InstructionComparator.InsnListSection;
|
|
|
|
|
import com.google.common.collect.HashMultimap;
|
|
|
|
|
import com.google.common.collect.Multimap;
|
2013-07-16 17:58:02 +10:00
|
|
|
import org.objectweb.asm.ClassReader;
|
|
|
|
|
import org.objectweb.asm.ClassWriter;
|
|
|
|
|
import org.objectweb.asm.Label;
|
2014-04-09 13:38:05 +10:00
|
|
|
import org.objectweb.asm.tree.*;
|
2013-07-16 17:58:02 +10:00
|
|
|
|
2014-04-09 13:38:05 +10:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
2013-07-16 17:58:02 +10:00
|
|
|
|
|
|
|
|
public class ASMHelper
|
2014-04-07 14:02:47 +10:00
|
|
|
{
|
2013-07-16 17:58:02 +10:00
|
|
|
public static class CodeBlock
|
|
|
|
|
{
|
|
|
|
|
public Label start = new Label();
|
|
|
|
|
public Label end = new Label();
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
public static class ForBlock extends CodeBlock
|
|
|
|
|
{
|
|
|
|
|
public Label cmp = new Label();
|
|
|
|
|
public Label inc = new Label();
|
|
|
|
|
public Label body = new Label();
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
public static abstract class MethodAltercator
|
|
|
|
|
{
|
|
|
|
|
public final ObfMapping method;
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public MethodAltercator(ObfMapping method) {
|
|
|
|
|
this.method = method.toClassloading();
|
2013-07-16 17:58:02 +10:00
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
public abstract void alter(MethodNode mv);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
public static abstract class MethodWriter
|
|
|
|
|
{
|
|
|
|
|
public final int access;
|
|
|
|
|
public final ObfMapping method;
|
|
|
|
|
public final String[] exceptions;
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public MethodWriter(int access, ObfMapping method) {
|
2013-07-16 17:58:02 +10:00
|
|
|
this(access, method, null);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public MethodWriter(int access, ObfMapping method, String[] exceptions) {
|
2013-07-16 17:58:02 +10:00
|
|
|
this.access = access;
|
|
|
|
|
this.method = method;
|
|
|
|
|
this.exceptions = exceptions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public abstract void write(MethodNode mv);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
public static class MethodInjector
|
|
|
|
|
{
|
|
|
|
|
public final ObfMapping method;
|
|
|
|
|
public final InsnList needle;
|
|
|
|
|
public final InsnList injection;
|
|
|
|
|
public final boolean before;
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public MethodInjector(ObfMapping method, InsnList needle, InsnList injection, boolean before) {
|
2013-07-16 17:58:02 +10:00
|
|
|
this.method = method;
|
|
|
|
|
this.needle = needle;
|
|
|
|
|
this.injection = injection;
|
|
|
|
|
this.before = before;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) {
|
|
|
|
|
for (MethodNode mnode : cnode.methods)
|
|
|
|
|
if (methodmap.matches(mnode))
|
2013-07-16 17:58:02 +10:00
|
|
|
return mnode;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode) {
|
|
|
|
|
for (FieldNode fnode : cnode.fields)
|
|
|
|
|
if (fieldmap.matches(fnode))
|
2013-07-16 17:58:02 +10:00
|
|
|
return fnode;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static ClassNode createClassNode(byte[] bytes) {
|
2013-07-16 17:58:02 +10:00
|
|
|
return createClassNode(bytes, 0);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static ClassNode createClassNode(byte[] bytes, int flags) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ClassNode cnode = new ClassNode();
|
|
|
|
|
ClassReader reader = new ClassReader(bytes);
|
|
|
|
|
reader.accept(cnode, flags);
|
|
|
|
|
return cnode;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static byte[] createBytes(ClassNode cnode, int flags) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ClassWriter cw = new CC_ClassWriter(flags);
|
|
|
|
|
cnode.accept(cw);
|
|
|
|
|
return cw.toByteArray();
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static byte[] writeMethods(String name, byte[] bytes, Multimap<String, MethodWriter> writers) {
|
|
|
|
|
if (writers.containsKey(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ClassNode cnode = createClassNode(bytes);
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
for (MethodWriter mw : writers.get(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
MethodNode mv = findMethod(mw.method, cnode);
|
2014-04-07 14:02:47 +10:00
|
|
|
if (mv == null)
|
2013-07-16 17:58:02 +10:00
|
|
|
mv = (MethodNode) cnode.visitMethod(mw.access, mw.method.s_name, mw.method.s_desc, null, mw.exceptions);
|
|
|
|
|
|
|
|
|
|
mv.access = mw.access;
|
|
|
|
|
mv.instructions.clear();
|
|
|
|
|
mw.write(mv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static byte[] injectMethods(String name, byte[] bytes, Multimap<String, MethodInjector> injectors) {
|
|
|
|
|
if (injectors.containsKey(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ClassNode cnode = createClassNode(bytes);
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
for (MethodInjector injector : injectors.get(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
MethodNode method = findMethod(injector.method, cnode);
|
2014-04-07 14:02:47 +10:00
|
|
|
if (method == null)
|
|
|
|
|
throw new RuntimeException("Method not found: " + injector.method);
|
2013-07-16 17:58:02 +10:00
|
|
|
System.out.println("Injecting into " + injector.method + "\n" + printInsnList(injector.injection));
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
List<AbstractInsnNode> callNodes;
|
2014-04-07 14:02:47 +10:00
|
|
|
if (injector.before)
|
2013-07-16 17:58:02 +10:00
|
|
|
callNodes = InstructionComparator.insnListFindStart(method.instructions, injector.needle);
|
|
|
|
|
else
|
|
|
|
|
callNodes = InstructionComparator.insnListFindEnd(method.instructions, injector.needle);
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
if (callNodes.size() == 0) {
|
|
|
|
|
throw new RuntimeException("Needle not found in Haystack: " + injector.method + "\n" + printInsnList(injector.needle));
|
2013-07-16 17:58:02 +10:00
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
for (AbstractInsnNode node : callNodes) {
|
|
|
|
|
if (injector.before) {
|
|
|
|
|
System.out.println("Injected before: " + printInsn(node));
|
2013-07-16 17:58:02 +10:00
|
|
|
method.instructions.insertBefore(node, cloneInsnList(injector.injection));
|
2014-04-07 14:02:47 +10:00
|
|
|
} else {
|
|
|
|
|
System.out.println("Injected after: " + printInsn(node));
|
2013-07-16 17:58:02 +10:00
|
|
|
method.instructions.insert(node, cloneInsnList(injector.injection));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bytes = createBytes(cnode, ClassWriter.COMPUTE_FRAMES);
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static String printInsnList(InsnList list) {
|
|
|
|
|
InsnListPrinter p = new InsnListPrinter();
|
2013-07-16 17:58:02 +10:00
|
|
|
p.visitInsnList(list);
|
|
|
|
|
return p.textString();
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static String printInsn(AbstractInsnNode insn) {
|
2013-07-16 17:58:02 +10:00
|
|
|
InsnListPrinter p = new InsnListPrinter();
|
|
|
|
|
p.visitInsn(insn);
|
|
|
|
|
return p.textString();
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static Map<LabelNode, LabelNode> cloneLabels(InsnList insns) {
|
2013-07-16 17:58:02 +10:00
|
|
|
HashMap<LabelNode, LabelNode> labelMap = new HashMap<LabelNode, LabelNode>();
|
2014-04-07 14:02:47 +10:00
|
|
|
for (AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
|
|
|
|
|
if (insn.getType() == 8)
|
2013-07-16 17:58:02 +10:00
|
|
|
labelMap.put((LabelNode) insn, new LabelNode());
|
|
|
|
|
return labelMap;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static InsnList cloneInsnList(InsnList insns) {
|
2013-07-16 17:58:02 +10:00
|
|
|
return cloneInsnList(cloneLabels(insns), insns);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList insns) {
|
2013-07-16 17:58:02 +10:00
|
|
|
InsnList clone = new InsnList();
|
2014-04-07 14:02:47 +10:00
|
|
|
for (AbstractInsnNode insn = insns.getFirst(); insn != null; insn = insn.getNext())
|
2013-07-16 17:58:02 +10:00
|
|
|
clone.add(insn.clone(labelMap));
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
return clone;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static List<TryCatchBlockNode> cloneTryCatchBlocks(Map<LabelNode, LabelNode> labelMap, List<TryCatchBlockNode> tcblocks) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ArrayList<TryCatchBlockNode> clone = new ArrayList<TryCatchBlockNode>();
|
2014-04-07 14:02:47 +10:00
|
|
|
for (TryCatchBlockNode node : tcblocks)
|
2013-07-16 17:58:02 +10:00
|
|
|
clone.add(new TryCatchBlockNode(
|
2014-04-07 14:02:47 +10:00
|
|
|
labelMap.get(node.start),
|
|
|
|
|
labelMap.get(node.end),
|
|
|
|
|
labelMap.get(node.handler),
|
2013-07-16 17:58:02 +10:00
|
|
|
node.type));
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
return clone;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap, List<LocalVariableNode> locals) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>();
|
2014-04-07 14:02:47 +10:00
|
|
|
for (LocalVariableNode node : locals)
|
2013-07-16 17:58:02 +10:00
|
|
|
clone.add(new LocalVariableNode(
|
2014-04-07 14:02:47 +10:00
|
|
|
node.name, node.desc, node.signature,
|
|
|
|
|
labelMap.get(node.start),
|
|
|
|
|
labelMap.get(node.end),
|
2013-07-16 17:58:02 +10:00
|
|
|
node.index));
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
return clone;
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static void copy(MethodNode src, MethodNode dst) {
|
2013-07-16 17:58:02 +10:00
|
|
|
Map<LabelNode, LabelNode> labelMap = cloneLabels(src.instructions);
|
|
|
|
|
dst.instructions = cloneInsnList(labelMap, src.instructions);
|
|
|
|
|
dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks);
|
2014-04-07 14:02:47 +10:00
|
|
|
if (src.localVariables != null)
|
2013-07-16 17:58:02 +10:00
|
|
|
dst.localVariables = cloneLocals(labelMap, src.localVariables);
|
|
|
|
|
dst.visibleAnnotations = src.visibleAnnotations;
|
|
|
|
|
dst.invisibleAnnotations = src.invisibleAnnotations;
|
|
|
|
|
dst.visitMaxs(src.maxStack, src.maxLocals);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static byte[] alterMethods(String name, byte[] bytes, HashMultimap<String, MethodAltercator> altercators) {
|
|
|
|
|
if (altercators.containsKey(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
ClassNode cnode = createClassNode(bytes);
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
for (MethodAltercator injector : altercators.get(name)) {
|
2013-07-16 17:58:02 +10:00
|
|
|
MethodNode method = findMethod(injector.method, cnode);
|
2014-04-07 14:02:47 +10:00
|
|
|
if (method == null)
|
|
|
|
|
throw new RuntimeException("Method not found: " + injector.method);
|
|
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
injector.alter(method);
|
|
|
|
|
}
|
2014-04-07 14:02:47 +10:00
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
bytes = createBytes(cnode, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
|
|
|
|
|
public static String printInsnList(InsnListSection subsection) {
|
|
|
|
|
InsnListPrinter p = new InsnListPrinter();
|
2013-07-16 17:58:02 +10:00
|
|
|
p.visitInsnList(subsection);
|
|
|
|
|
return p.textString();
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static int getLocal(List<LocalVariableNode> list, String name) {
|
2013-07-16 17:58:02 +10:00
|
|
|
int found = -1;
|
2014-04-07 14:02:47 +10:00
|
|
|
for (LocalVariableNode node : list) {
|
|
|
|
|
if (node.name.equals(name)) {
|
|
|
|
|
if (found >= 0)
|
|
|
|
|
throw new RuntimeException("Duplicate local variable: " + name + " not coded to handle this scenario.");
|
|
|
|
|
|
2013-07-16 17:58:02 +10:00
|
|
|
found = node.index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static void replaceMethodCode(MethodNode original, MethodNode replacement) {
|
2013-07-16 17:58:02 +10:00
|
|
|
original.instructions.clear();
|
2014-04-07 14:02:47 +10:00
|
|
|
if (original.localVariables != null)
|
2013-07-16 17:58:02 +10:00
|
|
|
original.localVariables.clear();
|
2014-04-07 14:02:47 +10:00
|
|
|
if (original.tryCatchBlocks != null)
|
2013-07-16 17:58:02 +10:00
|
|
|
original.tryCatchBlocks.clear();
|
|
|
|
|
replacement.accept(original);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 14:02:47 +10:00
|
|
|
public static void removeBlock(InsnList insns, InsnListSection block) {
|
2013-07-16 17:58:02 +10:00
|
|
|
AbstractInsnNode insn = block.first;
|
2014-04-07 14:02:47 +10:00
|
|
|
while (true) {
|
2013-07-16 17:58:02 +10:00
|
|
|
AbstractInsnNode next = insn.getNext();
|
|
|
|
|
insns.remove(insn);
|
2014-04-07 14:02:47 +10:00
|
|
|
if (insn == block.last)
|
2013-07-16 17:58:02 +10:00
|
|
|
break;
|
|
|
|
|
insn = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|