public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass) {
    Set<Integer> methodCallLocations = new HashSet<Integer>();
    Integer newCallLocation = null;
    Integer methodReflectionLocation = null;
    Integer fakeCallRequiredLocation = 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(Method.class.getName())) {
          if (methodName.equals("invoke")) {
            // store the location in the const pool of the method ref
            methodCallLocations.add(i);
            // we have found a method call

            // if we have not already stored a reference to our new
            // method in the const pool
            if (newCallLocation == null) {
              methodReflectionLocation =
                  pool.addClassInfo("org.fakereplace.reflection.MethodReflection");
              int nt = pool.addNameAndTypeInfo("fakeCallRequired", "(Ljava/lang/reflect/Method;)Z");
              fakeCallRequiredLocation = pool.addMethodrefInfo(methodReflectionLocation, nt);
              newCallLocation = pool.addNameAndTypeInfo(METHOD_NAME, REPLACED_METHOD_DESCRIPTOR);
            }
          }
        }
      }
    }

    // this means we found an instance of the call, now we have to iterate
    // through the methods and replace instances of the call
    if (newCallLocation != 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);
              // if the method call is one of the methods we are
              // replacing
              if (methodCallLocations.contains(val)) {
                Bytecode b = new Bytecode(file.getConstPool());
                // our stack looks like Method, instance,params
                // we need Method, instance, params , Method
                b.add(Opcode.DUP_X2);
                b.add(Opcode.POP);
                b.add(Opcode.DUP_X2);
                b.add(Opcode.POP);
                b.add(Opcode.DUP_X2);
                b.addInvokestatic(
                    methodReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Method;)Z");
                b.add(Opcode.IFEQ);
                JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                // now perform the fake call
                b.addInvokestatic(methodReflectionLocation, "invoke", REPLACED_METHOD_DESCRIPTOR);
                b.add(Opcode.GOTO);
                JumpMarker finish = JumpUtils.addJumpInstruction(b);
                performRealCall.mark();
                b.addInvokevirtual(Method.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.insert(b.get());
              }
            }
          }
          m.getCodeAttribute().computeMaxStack();
        } catch (Exception e) {
          log.error("Bad byte code transforming " + file.getName());
          e.printStackTrace();
        }
      }
      return true;
    } else {
      return false;
    }
  }
예제 #2
0
 public boolean match(String filename, ClassFile classFile, ClassMap tempClassMap) {
   for (Object o : classFile.getMethods()) {
     MethodInfo methodInfo = (MethodInfo) o;
     classMod.methodInfo = methodInfo;
     CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
     if (codeAttribute == null) {
       continue;
     }
     if (getClassMap().hasMap(deobfMethod)) {
       MethodRef obfTarget = (MethodRef) getClassMap().map(deobfMethod);
       if (!methodInfo.getName().equals(obfTarget.getName())) {
         continue;
       }
     }
     ArrayList<String> deobfTypes = null;
     ArrayList<String> obfTypes = null;
     if (deobfMethod != null && deobfMethod.getType() != null) {
       deobfTypes = ConstPoolUtils.parseDescriptor(deobfMethod.getType());
       obfTypes = ConstPoolUtils.parseDescriptor(methodInfo.getDescriptor());
       if (!isPotentialTypeMatch(deobfTypes, obfTypes)) {
         continue;
       }
     }
     ConstPool constPool = methodInfo.getConstPool();
     CodeIterator codeIterator = codeAttribute.iterator();
     initMatcher();
     ArrayList<JavaRef> tempMappings = new ArrayList<JavaRef>();
     try {
       match:
       for (int offset = 0;
           offset < codeIterator.getCodeLength() && matcher.match(methodInfo, offset);
           offset = codeIterator.next()) {
         tempMappings.clear();
         for (Map.Entry<Integer, JavaRef> entry : xrefs.entrySet()) {
           int captureGroup = entry.getKey();
           JavaRef xref = entry.getValue();
           byte[] code = matcher.getCaptureGroup(captureGroup);
           int index = Util.demarshal(code, 1, 2);
           ConstPoolUtils.matchOpcodeToRefType(code[0], xref);
           ConstPoolUtils.matchConstPoolTagToRefType(constPool.getTag(index), xref);
           JavaRef newRef = ConstPoolUtils.getRefForIndex(constPool, index);
           if (!isPotentialTypeMatch(xref.getType(), newRef.getType())) {
             if (deobfMethod != null) {
               Logger.log(
                   Logger.LOG_METHOD,
                   "method %s %s matches %s %s, but",
                   methodInfo.getName(),
                   methodInfo.getDescriptor(),
                   deobfMethod.getName(),
                   deobfMethod.getType());
             }
             Logger.log(
                 Logger.LOG_METHOD,
                 "method %s %s failed xref #%d %s %s -> %s %s",
                 methodInfo.getName(),
                 methodInfo.getDescriptor(),
                 captureGroup,
                 xref.getName(),
                 xref.getType(),
                 newRef.getName(),
                 newRef.getType());
             continue match;
           }
           tempMappings.add(xref);
           tempMappings.add(newRef);
         }
         for (int i = 0; i + 1 < tempMappings.size(); i += 2) {
           tempClassMap.addMap(tempMappings.get(i), tempMappings.get(i + 1));
         }
         if (deobfMethod != null) {
           String deobfName = classMod.getDeobfClass();
           tempClassMap.addClassMap(deobfName, ClassMap.filenameToClassName(filename));
           tempClassMap.addMethodMap(
               deobfName, deobfMethod.getName(), methodInfo.getName(), methodInfo.getDescriptor());
           if (deobfTypes != null && obfTypes != null) {
             for (int i = 0; i < deobfTypes.size(); i++) {
               String desc = ClassMap.descriptorToClassName(deobfTypes.get(i));
               String obf = ClassMap.descriptorToClassName(obfTypes.get(i));
               if (!obf.equals(desc)) {
                 tempClassMap.addClassMap(desc, obf);
               }
             }
           }
         }
         afterMatch(classFile, methodInfo);
         classMod.methodInfo = null;
         return true;
       }
     } catch (BadBytecode e) {
       Logger.log(e);
     }
   }
   classMod.methodInfo = null;
   return false;
 }