2016-05-31 97 views
2

我试图修改其打包jar文件不在类路径中的几个类的字节码 - 它们在运行时通过自定义ClassLoader加载给定URL。我试图用java agentClassFileTransformer希望拦截这些类,但失败。 classloader是遗留项目的一部分,因此我无法直接对其进行更改。如何仪器类加载自定义类加载器?

该代理对AppClassLoader“本地”加载的类正常工作,但只是忽略由自定义类加载器加载的类。

CustomClassLoader

public class CustomClassLoader extends URLClassLoader { 

    public CustomClassLoader(URL[] urls) { 
     super(urls, CustomClassLoader.class.getClassLoader()); 
    } 

    // violates parent-delegation pattern 
    @Override 
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 
     synchronized (getClassLoadingLock(name)) { 
      Class<?> clazz = findLoadedClass(name); 
      if (clazz == null) { 
       try { 
        clazz = findClass(name); 
       } catch (ClassNotFoundException e) { 
       } 

       if (clazz == null) { 
        clazz = getParent().loadClass(name); 
       } 
      } 
      if (resolve) { 
       resolveClass(clazz); 
      } 
      return clazz; 
     } 
    } 
} 

ClassFileTransformer在我的经纪人使用(用Javassist):

public class MyTransformer implements ClassFileTransformer 
{ 
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
      ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException 
    { 
     byte[] byteCode = null; 

     if (className.replace("/", ".").equals("com.example.services.TargetService")) 
     { 
      ClassPool cp = ClassPool.getDefault(); 
      CtClass cc; 
      try 
      { 
       cc = cp.get("com.example.services.TargetService"); 
       CtMethod verifyMethod = cc.getDeclaredMethod("verify"); 
       //invalidate the verification process of method : verify 
       verifyMethod.insertBefore("{return true;}"); 
       byteCode = cc.toBytecode(); 
       cc.detach(); 
       return byteCode; 
      } 
      catch (Exception e) 
      { 
       e.printStackTrace(); 
      } 
     } 

     return byteCode; 
    } 
} 

代理

public class Agent 
{ 
    public static void premain(String agentArgs, Instrumentation inst) 
    { 
     inst.addTransformer(new MyTransformer()); 
    } 

} 

我想出了一个解决方法,通过调用CustomClassLoader本身,调用instrumentation.redifineClasses(),但不知道如何将检测实例传递到CustomClassLoader实例中;我不熟悉仪器/类加载,但仍然不清楚其机制。 有什么帮助吗?谢谢。

为了使简单:

  1. 内的应用程序创建而在其他地方,你在运行时的文件系统加载某些jar文件自定义的URLClassLoader。
  2. 实现一个java代理程序,用于转换类加载器加载的类,替换其中一个方法的主体或其他东西。
  3. 在应用程序内部为其接口分配一个类的实例并调用它的检测方法。
  4. 使用-javaagent运行应用程序进行检查。
+0

你有没有依赖的简单代理尝试过吗?例如尝试简单地调用System.out.println(className);在你的方法转换中,然后返回classfileBuffer。我试图用你的CustomClassLoader加载一个类,我在控制台上看到这个类的名字 –

+0

@NicolasFilotto问题解决了,谢谢,你让我想起了我用于代理的工具。代理本身得到了类加载事件的通知,但如果外部URL未添加到其类路径中,则检测工具无法获取字节码。 –

回答

1

我认为你的班级没有正确安装,因为你打电话给ClassPool.getDefault(),它不包括对你的自定义类加载器可见的类文件,但只对系统类加载器可见。您永远不会注册classfileBuffer类文件。

作为替代方案,你可以尝试Byte Buddy它提供的仪表API更容易获得:

new AgentBuilder.Default() 
    .type(named("com.example.services.TargetService")) 
    .transform((builder, type, loader) -> { 
    builder.method(named("verify")).intercept(FixedValue.of(true)); 
    }).installOn(instrumentation); 

上述代理可以从agentmainpremain方法被调用。您也可以禁用类文件格式更改并重新定义现有类,以防在附件期间该类已被(可能)加载。

+1

这是类池问题,添加'cp.appendClassPath(新的LoaderClassPath(加载程序));'和工作,真的很感谢你,字节伙伴看起来很酷。如果我有足够的代表,我会很高兴。 –