Skip to content

Instantly share code, notes, and snippets.

@Rubisk
Created January 7, 2015 20:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rubisk/8036d66d767b4c0764cc to your computer and use it in GitHub Desktop.
Save Rubisk/8036d66d767b4c0764cc to your computer and use it in GitHub Desktop.
Code
package com.rubisk.colorsignmod;
import java.util.Iterator;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import static org.objectweb.asm.Opcodes.SIPUSH;
import net.minecraft.launchwrapper.IClassTransformer;
public class EDClassTransformer implements IClassTransformer {
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
//Bytecode manipulations goes here
if (arg0.equals("v")){
System.out.println("***PATCHING " + arg0 + " IN OBFUSCATED TRANSFORMER***");
return patchClassASM(arg0, arg2, true);
}
if (arg0.equals("net.minecraft.util.ChatAllowedCharacters")){
System.out.println("***PATCHING " + arg0 + " IN TRANSFORMER***");
return patchClassASM(arg0, arg2, false);
}
return arg2;
}
public byte[] patchClassASM(String name, byte[] bytes, boolean obfuscated) {
String targetMethodName = "";
//Our target method
if(obfuscated == true)
targetMethodName ="a";
else
targetMethodName ="isAllowedCharacter";
//set up ASM class manipulation stuff. Consult the ASM docs for details
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
//Now we loop over all of the methods declared inside the Explosion class until we get to the targetMethodName "doExplosionB"
Iterator<MethodNode> methods = classNode.methods.iterator();
while(methods.hasNext()){
MethodNode m = methods.next();
int fdiv_index = -1;
System.out.println(m.name);
//Check if this is doExplosionB and it's method signature is (Z)V which means that it accepts a boolean (Z) and returns a void (V)
if ((m.name.equals(targetMethodName) && m.desc.equals("(C)Z"))){
System.out.println("********* Inside target method!");
AbstractInsnNode currentNode = null;
AbstractInsnNode targetNode = null;
@SuppressWarnings("unchecked")
Iterator<AbstractInsnNode> iter = m.instructions.iterator();
int index = -1;
//Loop over the instruction set and find the instruction FDIV which checks for 167
while (iter.hasNext()){
index++;
currentNode = iter.next();
//Found it! save the index location of instruction FDIV and the node for this instruction
if (currentNode.getOpcode() == SIPUSH){
targetNode = currentNode;
fdiv_index = index;
}
}
//now we want the save nods that load the variable explosionSize and the division instruction:
/*
mv.visitInsn(FCONST_1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "net/minecraft/src/Explosion", "explosionSize", "F");
mv.visitInsn(FDIV);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/src/Block", "dropBlockAsItemWithChance", "(Lnet/minecraft/src/World;IIIIFI)V");
*/
AbstractInsnNode remNode1 = m.instructions.get(fdiv_index+1); // mv.visitFieldInsn(GETFIELD, "net/minecraft/src/Explosion", "explosionSize", "F");
AbstractInsnNode remNode2 = m.instructions.get(fdiv_index-1); // mv.visitFieldInsn(GETFIELD, "net/minecraft/src/Explosion", "explosionSize", "F");
AbstractInsnNode remNode3 = m.instructions.get(fdiv_index); // mv.visitInsn(FDIV);
//just remove these nodes from the instruction set, this will prevent the instruction FCONST_1 to be divided.
m.instructions.remove(remNode1);
m.instructions.remove(remNode2);
m.instructions.remove(remNode3);
//in this section, i'll just illustrate how to inject a call to a static method if your instruction is a little more advanced than just removing a couple of instruction:
/*
To add new instructions, such as calling a static method can be done like so:
// make new instruction list
InsnList toInject = new InsnList();
//add your own instruction lists: *USE THE ASM JAVADOC AS REFERENCE*
toInject.add(new VarInsnNode(ALOAD, 0));
toInject.add(new MethodInsnNode(INVOKESTATIC, "mod/culegooner/MyStaticClass", "myStaticMethod", "()V"));
// add the added code to the nstruction list
// You can also choose if you want to add the code before or after the target node, check the ASM Javadoc (insertBefore)
m.instructions.insert(targetNode, toInject);
*/
System.out.println("Patching Complete!");
break;
}
}
//ASM specific for cleaning up and returning the final bytes for JVM processing.
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
}
}
package com.rubisk.colorsignmod;
import java.util.Map;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
public class EDFMLLoadingPlugin implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
//This will return the name of the class "com.rubisk.EDClassTransformer"
return new String[]{EDClassTransformer.class.getName()};
}
@Override
public String getModContainerClass() {
//This is the name of our dummy container "mod.culegooner.CreeperBurnCore.CBDummyContainer"
return MyContainer.class.getName();
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
package com.rubisk.colorsignmod;
import java.util.Arrays;
import net.minecraftforge.fml.common.LoadController;
import net.minecraftforge.fml.common.ModMetadata;
import net.minecraftforge.fml.common.event.FMLConstructionEvent;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
public class MyContainer extends net.minecraftforge.fml.common.DummyModContainer {
public MyContainer() {
super(new ModMetadata());
ModMetadata meta = getMetadata();
meta.modId = "ColorSignMod";
meta.name = "Colored Section Signs Allowance";
meta.version = "1.0";
meta.credits = "";
meta.authorList = Arrays.asList("Rubisk_");
meta.description = "";
meta.url = "";
meta.updateUrl = "";
meta.screenshots = new String[0];
meta.logoFile = "";
}
@Override
public boolean registerBus(EventBus bus, LoadController controller) {
bus.register(this);
return true;
}
@Subscribe
public void modConstruction(FMLConstructionEvent evt){
}
@Subscribe
public void preInit(FMLPreInitializationEvent evt) {
}
@Subscribe
public void init(FMLInitializationEvent evt) {
}
@Subscribe
public void postInit(FMLPostInitializationEvent evt) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment