Search in sources :

Example 36 with MethodInfo

use of javassist.bytecode.MethodInfo in project fakereplace by fakereplace.

the class ConstructorInvocationManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<ConstructorRewriteData>> constructorRewrites = new HashMap<>(data.getManipulationData(loader));
    Map<Integer, ConstructorRewriteData> methodCallLocations = new HashMap<>();
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref) {
            boolean handled = false;
            String className = pool.getMethodrefClassName(i);
            String methodDesc = pool.getMethodrefType(i);
            String methodName = pool.getMethodrefName(i);
            if (methodName.equals("<init>")) {
                if (constructorRewrites.containsKey(className)) {
                    for (ConstructorRewriteData data : constructorRewrites.get(className)) {
                        if (methodDesc.equals(data.getMethodDesc())) {
                            // store the location in the const pool of the method ref
                            methodCallLocations.put(i, data);
                            // we have found a method call
                            // now lets replace it
                            handled = true;
                            break;
                        }
                    }
                }
                if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
                    // may be an added field
                    // if the field does not actually exist yet we just assume it is about to come into existence
                    // and rewrite it anyway
                    BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                    if (data != null) {
                        MethodData method = data.getMethodOrConstructor("<init>", methodDesc);
                        if (method == null) {
                            // this is a new method
                            // lets deal with it
                            int methodNo = MethodIdentifierStore.instance().getMethodNumber("<init>", methodDesc);
                            methodCallLocations.put(i, new ConstructorRewriteData(className, methodDesc, methodNo, loader));
                        }
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!methodCallLocations.isEmpty()) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKESPECIAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.containsKey(val)) {
                            ConstructorRewriteData data = methodCallLocations.get(val);
                            // so we currently have all the arguments sitting on the
                            // stack, and we need to jigger them into
                            // an array and then call our method. First thing to do
                            // is scribble over the existing
                            // instructions:
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            Bytecode bc = new Bytecode(file.getConstPool());
                            ManipulationUtils.pushParametersIntoArray(bc, data.getMethodDesc());
                            // so now our stack looks like unconstructed instance : array
                            // we need unconstructed instance : int : array : null
                            bc.addIconst(data.getMethodNo());
                            bc.add(Opcode.SWAP);
                            bc.add(Opcode.ACONST_NULL);
                            bc.addInvokespecial(data.getClazz(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
                            // and we have our bytecode
                            it.insert(bc.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodData(org.fakereplace.data.MethodData) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Example 37 with MethodInfo

use of javassist.bytecode.MethodInfo in project fakereplace by fakereplace.

the class ManipulationUtils method addBogusConstructorCall.

/**
 * add a bogus constructor call to a bytecode sequence so a constructor can
 * pass bytecode validation
 */
public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) {
    MethodInfo constructorToCall = null;
    for (Object meth : file.getMethods()) {
        MethodInfo m = (MethodInfo) meth;
        if (m.getName().equals("<init>")) {
            constructorToCall = m;
            break;
        }
    }
    if (constructorToCall == null) {
        return false;
    }
    // push this onto the stack
    code.add(Bytecode.ALOAD_0);
    String[] params = DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor());
    for (String p : params) {
        // int char short boolean byte
        switch(p) {
            case "I":
            case "C":
            case "S":
            case "Z":
            case "B":
                // push integer 0
                code.add(Opcode.ICONST_0);
                break;
            // long
            case "J":
                code.add(Opcode.LCONST_0);
                break;
            // double
            case "D":
                code.add(Opcode.DCONST_0);
                break;
            // float
            case "F":
                code.add(Opcode.FCONST_0);
                break;
            // arrays and reference types
            default:
                code.add(Opcode.ACONST_NULL);
                break;
        }
    }
    // all our args should be pushed onto the stack, call the constructor
    code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor());
    code.addNew(NoSuchMethodError.class.getName());
    code.add(Opcode.DUP);
    code.addInvokespecial(NoSuchMethodError.class.getName(), "<init>", "()V");
    code.add(Opcode.ATHROW);
    return true;
}
Also used : MethodInfo(javassist.bytecode.MethodInfo)

Example 38 with MethodInfo

use of javassist.bytecode.MethodInfo in project fakereplace by fakereplace.

the class FieldManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<Data>> addedFieldData = data.getManipulationData(loader);
    if (addedFieldData.isEmpty()) {
        return false;
    }
    Map<Integer, Data> fieldAccessLocations = new HashMap<>();
    // first we need to scan the constant pool looking for
    // CONST_Fieldref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a field reference
        if (pool.getTag(i) == ConstPool.CONST_Fieldref) {
            String className = pool.getFieldrefClassName(i);
            String fieldName = pool.getFieldrefName(i);
            String descriptor = pool.getFieldrefType(i);
            boolean handled = false;
            if (addedFieldData.containsKey(className)) {
                for (Data data : addedFieldData.get(className)) {
                    if (fieldName.equals(data.getName())) {
                        // store the location in the const pool of the method ref
                        fieldAccessLocations.put(i, data);
                        handled = true;
                        break;
                    }
                }
            }
            if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
                // may be an added field
                // if the field does not actually exist yet we just assume it is about to come into existence
                // and rewrite it anyway
                BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                if (data != null) {
                    FieldData field = data.getField(fieldName);
                    if (field == null) {
                        // this is a new field
                        // lets deal with it
                        int fieldNo = FieldReferenceDataStore.instance().getFieldNo(fieldName, descriptor);
                        Data fieldData = new Data(fieldNo, fieldName, descriptor, className, loader);
                        fieldAccessLocations.put(i, fieldData);
                        Transformer.getManipulator().rewriteInstanceFieldAccess(fieldNo, fieldName, descriptor, className, loader);
                        addedFieldData = this.data.getManipulationData(loader);
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!fieldAccessLocations.isEmpty()) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a field access
                    if (op == Opcode.PUTFIELD || op == Opcode.GETFIELD || op == Opcode.GETSTATIC || op == Opcode.PUTSTATIC) {
                        int val = it.s16bitAt(index + 1);
                        // if the field access is for an added field
                        if (fieldAccessLocations.containsKey(val)) {
                            Data data = fieldAccessLocations.get(val);
                            int arrayPos = file.getConstPool().addIntegerInfo(data.getArrayIndex());
                            // write over the field access with nop
                            it.writeByte(Opcode.NOP, index);
                            it.writeByte(Opcode.NOP, index + 1);
                            it.writeByte(Opcode.NOP, index + 2);
                            if (op == Opcode.PUTFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            } else if (op == Opcode.PUTSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.add(Opcode.SWAP);
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            }
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
                e.printStackTrace();
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) FieldData(org.fakereplace.data.FieldData) FieldData(org.fakereplace.data.FieldData) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Example 39 with MethodInfo

use of javassist.bytecode.MethodInfo in project fakereplace by fakereplace.

the class ReflectionConstructorAccessManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Set<Integer> methodCallLocations = new HashSet<>();
    Integer constructorReflectionLocation = null;
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref) {
            String className = pool.getMethodrefClassName(i);
            String methodName = pool.getMethodrefName(i);
            if (className.equals(Constructor.class.getName())) {
                if (methodName.equals(METHOD_NAME)) {
                    // store the location in the const pool of the method ref
                    methodCallLocations.add(i);
                    // method in the const pool
                    if (constructorReflectionLocation == null) {
                        constructorReflectionLocation = pool.addClassInfo(ConstructorReflection.class.getName());
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (constructorReflectionLocation != null) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKEVIRTUAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.contains(val)) {
                            Bytecode b = new Bytecode(file.getConstPool());
                            // our stack looks like Constructor,params
                            // we need Constructor, params, Constructor
                            b.add(Opcode.SWAP);
                            b.add(Opcode.DUP_X1);
                            b.addInvokestatic(constructorReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Constructor;)Z");
                            b.add(Opcode.IFEQ);
                            JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                            // now perform the fake call
                            b.addInvokestatic(constructorReflectionLocation, METHOD_NAME, REPLACED_METHOD_DESCRIPTOR);
                            b.add(Opcode.GOTO);
                            JumpMarker finish = JumpUtils.addJumpInstruction(b);
                            performRealCall.mark();
                            b.addInvokevirtual(Constructor.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR);
                            finish.mark();
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            it.insertEx(b.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) JumpMarker(org.fakereplace.util.JumpMarker) Constructor(java.lang.reflect.Constructor) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode) HashSet(java.util.HashSet)

Example 40 with MethodInfo

use of javassist.bytecode.MethodInfo in project fakereplace by fakereplace.

the class ClassLoaderInstrumentation method redefineClassLoader.

/**
 * This method instruments class loaders so that they can load our helper
 * classes.
 */
@SuppressWarnings("unchecked")
public static boolean redefineClassLoader(ClassFile classFile, Set<MethodInfo> modifiedMethods) throws BadBytecode {
    boolean modified = false;
    for (MethodInfo method : (List<MethodInfo>) classFile.getMethods()) {
        if (Modifier.isStatic(method.getAccessFlags())) {
            continue;
        }
        if (method.getName().equals("loadClass") && (method.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") || method.getDescriptor().equals("(Ljava/lang/String;Z)Ljava/lang/Class;"))) {
            modifiedMethods.add(method);
            modified = true;
            if (method.getCodeAttribute().getMaxLocals() < 4) {
                method.getCodeAttribute().setMaxLocals(4);
            }
            // now we instrument the loadClass
            // if the system requests a class from the generated class package
            // then
            // we check to see if it is already loaded.
            // if not we try and get the class definition from GlobalData
            // we do not need to delegate as GlobalData will only
            // return the data to the correct classloader.
            // if the data is not null then we define the class, link
            // it if requested and return it.
            final CodeIterator iterator = method.getCodeAttribute().iterator();
            final Bytecode b = new Bytecode(classFile.getConstPool());
            b.addAload(1);
            b.addAload(0);
            b.addInvokestatic(ClassLookupManager.class.getName(), "getClassData", "(Ljava/lang/String;Ljava/lang/Object;)[B");
            b.add(Opcode.DUP);
            b.add(Opcode.IFNULL);
            JumpMarker jumpEnd = JumpUtils.addJumpInstruction(b);
            // now we need to do the findLoadedClasses thing
            b.addAload(0);
            b.addAload(1);
            b.addInvokevirtual("java.lang.ClassLoader", "findLoadedClass", "(Ljava/lang/String;)Ljava/lang/Class;");
            b.add(Opcode.DUP);
            b.add(Opcode.IFNULL);
            JumpMarker notFound = JumpUtils.addJumpInstruction(b);
            b.add(Opcode.ARETURN);
            notFound.mark();
            b.add(Opcode.POP);
            b.addAstore(3);
            b.addAload(0);
            b.addAload(1);
            b.addAload(3);
            b.addIconst(0);
            b.addAload(3);
            b.add(Opcode.ARRAYLENGTH);
            b.addInvokevirtual("java.lang.ClassLoader", "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
            if (method.getDescriptor().equals("Ljava/lang/String;Z)Ljava/lang/Class;")) {
                b.addIload(2);
            } else {
                b.addIconst(0);
            }
            b.add(Opcode.IFEQ);
            final JumpMarker linkJumpEnd = JumpUtils.addJumpInstruction(b);
            b.add(Opcode.DUP);
            b.addAload(0);
            b.add(Opcode.SWAP);
            b.addInvokevirtual("java.lang.ClassLoader", "resolveClass", "(Ljava/lang/Class;)V");
            linkJumpEnd.mark();
            b.add(Opcode.ARETURN);
            jumpEnd.mark();
            b.add(Opcode.POP);
            if (!classFile.getName().startsWith("java.") && !classFile.getName().startsWith("com.sun") && !classFile.getName().startsWith("sun") && !classFile.getName().startsWith("jdk.internal")) {
                // now we need to check if this is a fakereplace class
                // and if so always delegate to the appropriate loader
                b.addAload(1);
                b.addLdc("org.fakereplace");
                b.addInvokevirtual(String.class.getName(), "startsWith", "(Ljava/lang/String;)Z");
                b.add(Opcode.IFEQ);
                JumpMarker notFakereplace = JumpUtils.addJumpInstruction(b);
                // so this is a fakereplace class, delegate to the system loader
                b.addInvokestatic(ClassLoader.class.getName(), "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
                b.addAload(1);
                b.addInvokevirtual(ClassLoader.class.getName(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
                b.add(Opcode.ARETURN);
                notFakereplace.mark();
            }
            iterator.insert(b.get());
            method.getCodeAttribute().computeMaxStack();
        }
    }
    return modified;
}
Also used : JumpMarker(org.fakereplace.util.JumpMarker) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) List(java.util.List) BadBytecode(javassist.bytecode.BadBytecode) Bytecode(javassist.bytecode.Bytecode)

Aggregations

MethodInfo (javassist.bytecode.MethodInfo)54 Bytecode (javassist.bytecode.Bytecode)28 BadBytecode (javassist.bytecode.BadBytecode)19 CodeIterator (javassist.bytecode.CodeIterator)18 CodeAttribute (javassist.bytecode.CodeAttribute)17 ConstPool (javassist.bytecode.ConstPool)17 ClassFile (javassist.bytecode.ClassFile)12 LocalVariableAttribute (javassist.bytecode.LocalVariableAttribute)10 DuplicateMemberException (javassist.bytecode.DuplicateMemberException)9 List (java.util.List)7 ClassPool (javassist.ClassPool)7 ByteArrayOutputStream (java.io.ByteArrayOutputStream)6 DataOutputStream (java.io.DataOutputStream)6 IOException (java.io.IOException)6 CtMethod (javassist.CtMethod)6 HashMap (java.util.HashMap)5 HashSet (java.util.HashSet)5 Set (java.util.Set)5 AnnotationsAttribute (javassist.bytecode.AnnotationsAttribute)5 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)4