Created
January 7, 2015 20:05
-
-
Save Rubisk/8036d66d767b4c0764cc to your computer and use it in GitHub Desktop.
Code
This file contains hidden or 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 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(); | |
} | |
} |
This file contains hidden or 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 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; | |
} | |
} |
This file contains hidden or 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 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