/* Copyright 2012 Jan Ove Saltvedt This file is part of KBot. KBot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. KBot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KBot. If not, see <http://www.gnu.org/licenses/>. */ package com.kbotpro.bot.injector; import com.kbotpro.hooks.Model; import com.kbotpro.hooks.ModelWrapper; import com.kbotpro.interfaces.ClientCallback; import com.sun.org.apache.bcel.internal.Constants; import com.sun.org.apache.bcel.internal.classfile.ClassParser; import com.sun.org.apache.bcel.internal.classfile.Field; import com.sun.org.apache.bcel.internal.classfile.Method; import com.sun.org.apache.bcel.internal.generic.*; import com.sun.org.apache.bcel.internal.util.InstructionFinder; import org.apache.log4j.Logger; import org.jdom.Document; import org.jdom.Element; import javax.swing.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; /** * Created by IntelliJ IDEA. * User: Jan Ove Saltvedt * Date: Oct 17, 2009 * Time: 1:24:27 PM * To change this template use File | Settings | File Templates. */ public class Injector { public static ClassData inject(ClassData inputData, Document doc) { try { ClassGen cG = new ClassGen(new ClassParser(new ByteArrayInputStream(inputData.classData), inputData.name).parse()); Element root = doc.getRootElement(); Element classesElement = root.getChild("classes"); if (cG.getClassName().equals("client")) { if (!isVersion(cG, Integer.parseInt(root.getChildText("RSVersion")))) { JOptionPane.showMessageDialog(null, "This version of Runescape is not yet supported\nPlease wait patiently for an update.", "Outdated!", JOptionPane.ERROR_MESSAGE); throw new IllegalStateException("Not supported yet."); } } for (Element classNode : (List<Element>) classesElement.getChildren("class")) { if (classNode.getChildText("className").equals(cG.getClassName())) { for (Element taskElement : (List<Element>) classNode.getChildren("task")) { String type = taskElement.getChildText("type"); if (type.equals("injectInterface")) { Element data = taskElement.getChild("data"); String interfaceClassName = data.getChildText("interfaceClassName"); cG.getInterfaceNames(); cG.addInterface(interfaceClassName); } else if (type.equals("getter")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String fieldClassName = data.getChildText("fieldClassName"); String fieldName = data.getChildText("fieldName"); String fieldSignature = data.getChildText("fieldSignature"); int fieldAccessFlags = Integer.parseInt(data.getChildText("fieldAccessFlags")); String returnSignature = data.getChildText("returnSignature"); injectSimpleGetter(cG, methodName, fieldClassName, fieldName, fieldSignature, fieldAccessFlags, returnSignature); } else if (type.equals("setter")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String fieldName = data.getChildText("fieldName"); String fieldSignature = data.getChildText("fieldSignature"); String argumentsSignature = data.getChildText("argumentsSignature"); int fieldAccessFlags = Integer.parseInt(data.getChildText("fieldAccessFlags")); int methodAccessFlags = Integer.parseInt(data.getChildText("methodAccessFlags")); injectSimpleSetter(cG, methodName, argumentsSignature, fieldName, fieldSignature, fieldAccessFlags, methodAccessFlags); } else if (type.equals("injectField")) { Element data = taskElement.getChild("data"); String fieldName = data.getChildText("fieldName"); String fieldSignature = data.getChildText("fieldSignature"); int fieldAccessFlags = Integer.parseInt(data.getChildText("fieldAccessFlags")); injectField(cG, fieldName, fieldSignature, fieldAccessFlags); } else if (type.equals("serverMessageCallback")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); int injectionPos = Integer.parseInt(data.getChildText("injectionPos")); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); makeServerMessageCallback(cG, methodName, methodSignature, aloadIndex, injectionPos); } else if (type.equals("updateRenderDataCallback")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); makeUpdateRenderDataCallback(cG, methodName, methodSignature); } else if (type.equals("2LevelGetter")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String field1ClassName = data.getChildText("field1ClassName"); String field1Name = data.getChildText("field1Name"); String field1Signature = data.getChildText("field1Signature"); int field1AccessFlags = Integer.parseInt(data.getChildText("field1AccessFlags")); String field2ClassName = data.getChildText("field2ClassName"); String field2Name = data.getChildText("field2Name"); String field2Signature = data.getChildText("field2Signature"); int field2AccessFlags = Integer.parseInt(data.getChildText("field2AccessFlags")); String returnSignature = data.getChildText("returnSignature"); createLevel2Getter(cG, methodName, field1ClassName, field1Name, field1Signature, field1AccessFlags, field2ClassName, field2Name, field2Signature, field2AccessFlags, returnSignature); } else if (type.equals("returnSelf")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String returnSignature = data.getChildText("returnSignature"); injectReturnSelf(cG, methodName, returnSignature); } else if (type.equals("IComponentMastersHook")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); String iCompClassName = data.getChildText("IComponentClassName"); int injectionPos = Integer.parseInt(data.getChildText("injectionPos")); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); int iloadXIndex = Integer.parseInt(data.getChildText("iloadXIndex")); int iloadYIndex = Integer.parseInt(data.getChildText("iloadYIndex")); injectMasterXHook(cG, methodName, methodSignature, aloadIndex, iloadXIndex, iloadYIndex, iCompClassName, injectionPos); } else if (type.equals("returnInteger")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); int returnValue = Integer.parseInt(data.getChildText("returnValue")); injectReturnInteger(cG, methodName, returnValue); } else if (type.equals("returnNull")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String returnSignature = data.getChildText("returnSignature"); injectReturnNull(cG, methodName, returnSignature); } else if (type.equals("CharacterModelDumper")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); int injectionPos = Integer.parseInt(data.getChildText("injectionPos")); String modelClassName = data.getChildText("modelClassName"); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); characterModelDumper(cG, methodName, methodSignature, injectionPos, modelClassName, aloadIndex); } else if (type.equals("ModelDumper")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); int injectionPos = Integer.parseInt(data.getChildText("injectionPos")); String modelClassName = data.getChildText("modelClassName"); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); String fieldName = data.getChildText("fieldName"); modelDumper(cG, methodName, methodSignature, injectionPos, modelClassName, aloadIndex, fieldName); } else if (type.equals("IComponentVisibleHook")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); String iComponentClassNAme = data.getChildText("IComponentClassName"); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); String fieldName = data.getChildText("fieldFieldName"); String fieldClassName = data.getChildText("fieldClassName"); String loopFieldName = data.getChildText("loopFieldName"); String loopFieldClassName = data.getChildText("loopClassName"); injectIComponentVisibleHook(cG, methodName, methodSignature, iComponentClassNAme, aloadIndex, fieldClassName, fieldName, loopFieldClassName, loopFieldName); } else if (type.equals("CondVoidDisabler")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); String condMethodName = data.getChildText("condMethodName"); conditionalDisableVoidMethod(cG, methodName, methodSignature, condMethodName); } else if (type.equals("AnimatedModelDumper")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); animatedModelDumper(cG, methodName, methodSignature); } else if (type.equals("CharacterModelDumperOnce")) { Element data = taskElement.getChild("data"); String methodName = data.getChildText("methodName"); String methodSignature = data.getChildText("methodSignature"); int injectionPos = Integer.parseInt(data.getChildText("injectionPos")); String modelClassName = data.getChildText("modelClassName"); int aloadIndex = Integer.parseInt(data.getChildText("aloadIndex")); characterModelDumperOnce(cG, methodName, methodSignature, injectionPos, modelClassName, aloadIndex); } } break; } } inputData.classData = cG.getJavaClass().getBytes(); inputData.offset = 0; inputData.length = inputData.classData.length; } catch (IOException e) { Logger.getRootLogger().error("Exception: ", e); //To change body of catch statement use File | Settings | File Templates. } return inputData; } private static void characterModelDumperOnce(ClassGen cG, String methodName, String methodSignature, int injectionPos, String modelClassName, int aloadIndex) { final ConstantPoolGen cPool = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPool); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFactory iFac = new InstructionFactory(cG, cPool); ObjectType modelWrapperType = (ObjectType) Type.getType(ModelWrapper.class); InstructionList createNewIList = new InstructionList(); final InstructionHandle firstHandle = createNewIList.append(InstructionConstants.ALOAD_0); createNewIList.append(iFac.createNew(modelWrapperType)); createNewIList.append(new DUP()); createNewIList.append(new ALOAD(aloadIndex)); Type[] args = new Type[]{Type.getType(Model.class)}; createNewIList.append(iFac.createInvoke(modelWrapperType.getClassName(), "<init>", Type.VOID, args, Constants.INVOKESPECIAL)); final InstructionHandle putfieldHandle = createNewIList.append(iFac.createPutField(cG.getClassName(), "model", modelWrapperType)); InstructionList flowIList = new InstructionList(); /*if(!cG.getClassName().equals("cd")){ flowIList.append(iFac.createPrintln("Dumping model: "+cG.getClassName())); }*/ flowIList.append(InstructionConstants.ALOAD_0); flowIList.append(iFac.createGetField(cG.getClassName(), "model", modelWrapperType)); final InstructionHandle createNewIListStartHandle = flowIList.append(createNewIList); int index = 0; final int[] positions = mInstructionList.getInstructionPositions(); for (; index < positions.length; index++) { if (positions[index] == injectionPos) { break; } } mInstructionList.append(mInstructionList.getInstructionHandles()[index], flowIList); mInstructionList.insert(createNewIListStartHandle,new IFNONNULL(putfieldHandle.getNext())); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } private static void animatedModelDumper(ClassGen cG, String methodName, String methodSignature) { final ConstantPoolGen cPool = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPool); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFactory iFac = new InstructionFactory(cG, cPool); ObjectType modelWrapperType = (ObjectType) Type.getType(ModelWrapper.class); InstructionList createNewIList = new InstructionList(); int aloadIndex = methodGen.getMaxLocals(); final InstructionHandle firstHandle = createNewIList.append(InstructionConstants.ALOAD_0); createNewIList.append(iFac.createNew(modelWrapperType)); createNewIList.append(new DUP()); createNewIList.append(new ALOAD(aloadIndex)); Type[] args = new Type[]{Type.getType(Model.class)}; createNewIList.append(iFac.createInvoke(modelWrapperType.getClassName(), "<init>", Type.VOID, args, Constants.INVOKESPECIAL)); final InstructionHandle putfieldHandle = createNewIList.append(iFac.createPutField(cG.getClassName(), "model", modelWrapperType)); InstructionList updateOldIList = new InstructionList(); updateOldIList.append(InstructionConstants.ALOAD_0); updateOldIList.append(iFac.createGetField(cG.getClassName(), "model", modelWrapperType)); updateOldIList.append(new ALOAD(aloadIndex)); updateOldIList.append(iFac.createInvoke(modelWrapperType.getClassName(), "updateModelData", Type.VOID, args, Constants.INVOKEVIRTUAL)); InstructionList flowIList = new InstructionList(); flowIList.append(new ASTORE(aloadIndex)); //flowIList.append(iFac.createPrintln("Invoked getModel in class "+cG.getClassName())); flowIList.append(InstructionConstants.ALOAD_0); flowIList.append(iFac.createGetField(cG.getClassName(), "model", modelWrapperType)); flowIList.append(new IFNULL(firstHandle)); flowIList.append(updateOldIList); final InstructionHandle createNewIListStartHandle = flowIList.append(createNewIList); flowIList.append(new ALOAD(aloadIndex)); for(InstructionHandle handle: mInstructionList.getInstructionHandles()){ if(handle.getInstruction() instanceof ARETURN){ mInstructionList.insert(handle, flowIList); mInstructionList.insert(createNewIListStartHandle, new GOTO(putfieldHandle.getNext())); continue; } } methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } private static void conditionalDisableVoidMethod(ClassGen cG, String methodName, String methodSignature, String condMethodName) { ConstantPoolGen cpg = cG.getConstantPool(); for (Method method : cG.getMethods()) { if (method.getName().equals(methodName) && method.getSignature().equals(methodSignature)) { MethodGen methodGen = new MethodGen(method, cG.getClassName(), cpg); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFactory instructionFactory = new InstructionFactory(cG); InstructionList iList = new InstructionList(); iList.append(instructionFactory.createGetStatic("client", "callback", Type.getType(ClientCallback.class))); iList.append(instructionFactory.createInvoke("com/kbotpro/interfaces/ClientCallback", condMethodName, Type.BOOLEAN, new Type[]{}, Constants.INVOKEINTERFACE)); iList.append(new IFEQ(mInstructionList.getStart())); iList.append(new RETURN()); mInstructionList.insert(iList); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); cG.setConstantPool(cpg); } } } private static void injectIComponentVisibleHook(ClassGen cG, String methodName, String methodSignature, String iComponentClassName, int aloadIndex, String fieldClassName, String fieldName, String loopFieldClassName, String loopFieldName) { final ConstantPoolGen cpg = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } InstructionFactory instructionFactory = new InstructionFactory(cG); MethodGen methodGen = new MethodGen(method, cG.getClassName(), cpg); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFinder instructionFinder = new InstructionFinder(mInstructionList); for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("getstatic iload baload ((ifeq)|(ifne))"); iterator.hasNext();) { InstructionHandle[] ih = iterator.next(); FieldInstruction fieldInstruction = (FieldInstruction) ih[0].getInstruction(); if (fieldInstruction.getClassName(cpg).equals(fieldClassName) && fieldInstruction.getFieldName(cpg).equals(fieldName)) { InstructionHandle ifInsHandle = ih[ih.length - 1]; IfInstruction ifInstruction = (IfInstruction) ifInsHandle.getInstruction(); /* Inject the icomponent.visible = true; */ InstructionList iList1 = new InstructionList(); iList1.append(new ALOAD(aloadIndex)); //iList1.append(instructionFactory.createConstant(true)); // TRUE iList1.append(instructionFactory.createFieldAccess(loopFieldClassName, loopFieldName, Type.INT, Constants.GETSTATIC)); iList1.append(instructionFactory.createFieldAccess(iComponentClassName, "visibleLoopCycleStatus", Type.INT, Constants.PUTFIELD)); //iList1.append(instructionFactory.createPrintln("Called!")); InstructionHandle target = ifInstruction.getTarget(); if (ifInstruction instanceof IFEQ) { mInstructionList.append(ifInsHandle, iList1); } else { mInstructionList.append(target, iList1); } } } /*for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("aaload astore"); iterator.hasNext();) { InstructionHandle[] ih = iterator.next(); ASTORE astore = (ASTORE) ih[1].getInstruction(); if (astore.getIndex() != aloadIndex) { continue; } InstructionList iList2 = new InstructionList(); iList2.append(new ALOAD(aloadIndex)); iList2.append(instructionFactory.createConstant(false)); // FALSE iList2.append(instructionFactory.createFieldAccess(iComponentClassName, "visible", Type.BOOLEAN, Constants.PUTFIELD)); mInstructionList.append(ih[1], iList2); }*/ methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } private static void modelDumper(ClassGen cG, String methodName, String methodSignature, int injectionPos, String modelClassName, int aloadIndex, String fieldName) { final ConstantPoolGen cPool = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPool); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionList tempInstructionList = new InstructionList(); InstructionFactory iFac = new InstructionFactory(cG, cPool); tempInstructionList.append(InstructionConstants.ALOAD_0); ObjectType modelType = (ObjectType) Type.getType("L" + modelClassName + ";"); tempInstructionList.append(new ALOAD(aloadIndex)); tempInstructionList.append(iFac.createPutField(cG.getClassName(), fieldName, modelType)); int index = 0; final int[] positions = mInstructionList.getInstructionPositions(); for (; index < positions.length; index++) { if (positions[index] == injectionPos) { break; } } mInstructionList.append(mInstructionList.getInstructionHandles()[index], tempInstructionList); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } private static void characterModelDumper(ClassGen cG, String methodName, String methodSignature, int injectionPos, String modelClassName, int aloadIndex) { final ConstantPoolGen cPool = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPool); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFactory iFac = new InstructionFactory(cG, cPool); ObjectType modelWrapperType = (ObjectType) Type.getType(ModelWrapper.class); InstructionList createNewIList = new InstructionList(); final InstructionHandle firstHandle = createNewIList.append(InstructionConstants.ALOAD_0); createNewIList.append(iFac.createNew(modelWrapperType)); createNewIList.append(new DUP()); createNewIList.append(new ALOAD(aloadIndex)); Type[] args = new Type[]{Type.getType(Model.class)}; createNewIList.append(iFac.createInvoke(modelWrapperType.getClassName(), "<init>", Type.VOID, args, Constants.INVOKESPECIAL)); final InstructionHandle putfieldHandle = createNewIList.append(iFac.createPutField(cG.getClassName(), "model", modelWrapperType)); InstructionList updateOldIList = new InstructionList(); updateOldIList.append(InstructionConstants.ALOAD_0); updateOldIList.append(iFac.createGetField(cG.getClassName(), "model", modelWrapperType)); updateOldIList.append(new ALOAD(aloadIndex)); updateOldIList.append(iFac.createInvoke(modelWrapperType.getClassName(), "updateModelData", Type.VOID, args, Constants.INVOKEVIRTUAL)); InstructionList flowIList = new InstructionList(); /*if(!cG.getClassName().equals("cd")){ flowIList.append(iFac.createPrintln("Dumping model: "+cG.getClassName())); } */ flowIList.append(InstructionConstants.ALOAD_0); flowIList.append(iFac.createGetField(cG.getClassName(), "model", modelWrapperType)); flowIList.append(new IFNULL(firstHandle)); flowIList.append(updateOldIList); final InstructionHandle createNewIListStartHandle = flowIList.append(createNewIList); int index = 0; final int[] positions = mInstructionList.getInstructionPositions(); for (; index < positions.length; index++) { if (positions[index] == injectionPos) { break; } } mInstructionList.append(mInstructionList.getInstructionHandles()[index], flowIList); mInstructionList.insert(createNewIListStartHandle, new GOTO(putfieldHandle.getNext())); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } private static void injectReturnNull(ClassGen cG, String methodName, String returnSignature) { ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.getType(returnSignature), Type.NO_ARGS, new String[]{}, methodName, cG.getClassName(), iList, cp); InstructionFactory instructionFactory = new InstructionFactory(cG, cp); iList.append(new ACONST_NULL()); iList.append(new ARETURN()); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static void injectReturnInteger(ClassGen cG, String methodName, int returnValue) { ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.INT, Type.NO_ARGS, new String[]{}, methodName, cG.getClassName(), iList, cp); InstructionFactory instructionFactory = new InstructionFactory(cG, cp); iList.append(new SIPUSH((short) returnValue)); iList.append(new IRETURN()); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static boolean isVersion(ClassGen cG, int version) { for (Method m : cG.getMethods()) { if (!m.getName().equals("main")) { continue; } InstructionList iList = new InstructionList(m.getCode().getCode()); InstructionFinder instructionFinder = new InstructionFinder(iList); InstructionFinder.CodeConstraint codeConstraint = new InstructionFinder.CodeConstraint() { public boolean checkCode(InstructionHandle[] match) { int value = ((SIPUSH) match[0].getInstruction()).getValue().intValue(); return value >= 550 && value <= 700; } }; for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("sipush", codeConstraint); iterator.hasNext();) { InstructionHandle[] ih = iterator.next(); int value = ((SIPUSH) ih[0].getInstruction()).getValue().intValue(); return value == version; } } return false; } private static void injectReturnSelf(ClassGen cG, String methodName, String returnSignature) { ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.getType(returnSignature), Type.NO_ARGS, new String[]{}, methodName, cG.getClassName(), iList, cp); InstructionFactory instructionFactory = new InstructionFactory(cG, cp); iList.append(new ALOAD(0)); iList.append(instructionFactory.createCheckCast((ReferenceType) Type.getType(returnSignature))); iList.append(new ARETURN()); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static void createLevel2Getter(ClassGen cG, String methodName, String field1ClassName, String field1Name, String field1Signature, int field1AccessFlags, String field2ClassName, String field2Name, String field2Signature, int field2AccessFlags, String returnSignature) { boolean isField1Static = (field1AccessFlags & Constants.ACC_STATIC) != 0; boolean isField2Static = (field2AccessFlags & Constants.ACC_STATIC) != 0; ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.getType(returnSignature), Type.NO_ARGS, new String[]{}, methodName, cG.getClassName(), iList, cp); InstructionFactory iFact = new InstructionFactory(cG, cp); Instruction pushSelf = new ALOAD(0); Instruction getFirstField; if (isField1Static) getFirstField = iFact.createFieldAccess(field1ClassName, field1Name, Type.getType(field1Signature), Constants.GETSTATIC); else getFirstField = iFact.createFieldAccess(field1ClassName, field1Name, Type.getType(field1Signature), Constants.GETFIELD); Instruction getSecondField; if (isField2Static) getSecondField = iFact.createFieldAccess(field2ClassName, field2Name, Type.getType(field2Signature), Constants.GETSTATIC); else getSecondField = iFact.createFieldAccess(field2ClassName, field2Name, Type.getType(field2Signature), Constants.GETFIELD); Instruction returner = InstructionFactory.createReturn(Type.getType(returnSignature)); if (!isField1Static) { iList.append(pushSelf); } iList.append(getFirstField); iList.append(getSecondField); if (field2Signature.equals("I") && returnSignature.equals("F")) { iList.append(new I2F()); } else if (field2Signature.equals("I") && returnSignature.equals("S")) { iList.append(new I2S()); } iList.append(returner); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static void makeUpdateRenderDataCallback(ClassGen cG, String methodName, String methodSignature) { ConstantPoolGen cpg = cG.getConstantPool(); for (Method method : cG.getMethods()) { if (method.getName().equals(methodName) && method.getSignature().equals(methodSignature)) { MethodGen methodGen = new MethodGen(method, cG.getClassName(), cpg); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionFinder instructionFinder = new InstructionFinder(mInstructionList); for (Iterator<InstructionHandle[]> iterator = instructionFinder.search("return"); iterator.hasNext();) { InstructionHandle[] ih = iterator.next(); InstructionList newList = new InstructionList(); InstructionFactory instructionFactory = new InstructionFactory(cG, cpg); newList.append(instructionFactory.createGetStatic("client", "callback", Type.getType(ClientCallback.class))); newList.append(instructionFactory.createInvoke("com/kbotpro/interfaces/ClientCallback", "updateRenderData", Type.VOID, new Type[]{}, Constants.INVOKEINTERFACE)); InstructionHandle injectHandle = mInstructionList.insert(ih[0], newList); if (ih[0].hasTargeters()) { for (InstructionTargeter targeter : ih[0].getTargeters()) { targeter.updateTarget(ih[0], injectHandle); } } mInstructionList.setPositions(); } methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); cG.setConstantPool(cpg); } } } private static void injectField(ClassGen cG, String fieldName, String fieldSignature, int fieldAccessFlags) { ConstantPoolGen cpg = cG.getConstantPool(); cG.addField(new Field(fieldAccessFlags, cpg.addUtf8(fieldName), cpg.addUtf8(fieldSignature), null, cpg.getConstantPool())); } private static void makeServerMessageCallback(ClassGen cG, String methodName, String methodSignature, int aloadIndex, int injectionPos) { ConstantPoolGen cpg = cG.getConstantPool(); for (Method method : cG.getMethods()) { if (method.getName().equals(methodName) && method.getSignature().equals(methodSignature)) { MethodGen methodGen = new MethodGen(method, cG.getClassName(), cpg); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionList newList = new InstructionList(); InstructionFactory instructionFactory = new InstructionFactory(cG, cpg); newList.append(instructionFactory.createGetStatic("client", "callback", Type.getType(ClientCallback.class))); newList.append(new ALOAD(aloadIndex)); newList.append(instructionFactory.createInvoke("com/kbotpro/interfaces/ClientCallback", "serverMessage", Type.VOID, new Type[]{Type.STRING}, Constants.INVOKEINTERFACE)); int index = 0; final int[] positions = mInstructionList.getInstructionPositions(); for (; index < positions.length; index++) { if (positions[index] == injectionPos) { break; } } mInstructionList.insert(mInstructionList.getInstructionHandles()[index].getNext(), newList); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); cG.setConstantPool(cpg); } } } private static void injectSimpleSetter(ClassGen cG, String methodName, String argumentsSignature, String fieldName, String fieldSignature, int fieldAccessFlags, int methodAccessFlags) { boolean staticField = (fieldAccessFlags & Constants.ACC_STATIC) != 0; boolean staticMethod = (methodAccessFlags & Constants.ACC_STATIC) != 0; ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(methodAccessFlags, Type.VOID, Type.getArgumentTypes("(" + argumentsSignature + ")V"), null, methodName, cG.getClassName(), iList, cp); InstructionFactory iFact = new InstructionFactory(cG, cp); Instruction thisPush = new ALOAD(0); Instruction set; if (staticField) set = iFact.createFieldAccess(cG.getClassName(), fieldName, Type.getType(fieldSignature), Constants.PUTSTATIC); else set = iFact.createFieldAccess(cG.getClassName(), fieldName, Type.getType(fieldSignature), Constants.PUTFIELD); Instruction returner = InstructionFactory.createReturn(Type.VOID); if (!staticField) { iList.append(thisPush); } int localIndex = staticMethod ? 0 : 1; if (Type.getType(argumentsSignature) instanceof ObjectType) { iList.append(new ALOAD(localIndex)); } else { iList.append(new ILOAD(localIndex)); } iList.append(set); iList.append(returner); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static void injectSimpleGetter(ClassGen cG, String methodName, String fieldClassName, String fieldName, String fieldSignature, int fieldAccessFlags, String returnSignature) { boolean isStatic = (fieldAccessFlags & Constants.ACC_STATIC) != 0; ConstantPoolGen cp = cG.getConstantPool(); InstructionList iList = new InstructionList(); MethodGen method = new MethodGen(Constants.ACC_PUBLIC, Type.getType(returnSignature), Type.NO_ARGS, new String[]{}, methodName, cG.getClassName(), iList, cp); InstructionFactory iFact = new InstructionFactory(cG, cp); Instruction pushThis = new ALOAD(0); Instruction get; if (isStatic) get = iFact.createFieldAccess(fieldClassName, fieldName, Type.getType(fieldSignature), Constants.GETSTATIC); else get = iFact.createFieldAccess(fieldClassName, fieldName, Type.getType(fieldSignature), Constants.GETFIELD); Instruction returner = InstructionFactory.createReturn(Type.getType(returnSignature)); if (!isStatic) { iList.append(pushThis); } iList.append(get); if (fieldSignature.equals("I") && returnSignature.equals("F")) { iList.append(new I2F()); } iList.append(returner); method.setMaxStack(); method.setMaxLocals(); cG.addMethod(method.getMethod()); } private static void injectMasterXHook(ClassGen cG, String methodName, String methodSignature, int aloadIndex, int iloadXIndex, int iloadYIndex, String iCompClassName, int injectionPos) { final ConstantPoolGen cPool = cG.getConstantPool(); Method method = null; for (Method m : cG.getMethods()) { if (m.getName().equals(methodName) && m.getSignature().equals(methodSignature)) { method = m; } } MethodGen methodGen = new MethodGen(method, cG.getClassName(), cPool); InstructionList mInstructionList = methodGen.getInstructionList(); InstructionList tempInstructionList = new InstructionList(); InstructionFactory iFac = new InstructionFactory(cG, cPool); tempInstructionList.append(new ILOAD(iloadXIndex)); tempInstructionList.append(iFac.createPutField(iCompClassName, "masterX", Type.INT)); tempInstructionList.append(new ALOAD(aloadIndex)); tempInstructionList.append(new ILOAD(iloadYIndex)); tempInstructionList.append(iFac.createPutField(iCompClassName, "masterY", Type.INT)); tempInstructionList.append(new ALOAD(aloadIndex)); int index = 0; final int[] positions = mInstructionList.getInstructionPositions(); for (; index < positions.length; index++) { if (positions[index] == injectionPos) { break; } } mInstructionList.append(mInstructionList.getInstructionHandles()[index], tempInstructionList); methodGen.setMaxStack(); methodGen.setMaxLocals(); methodGen.update(); cG.replaceMethod(method, methodGen.getMethod()); } }