如何检测自定义类加载器加载的类?

时间:2016-05-31 10:01:07

标签: java classloader instrumentation javassist javaagents

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

代理可以在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. 在应用程序内部创建一个自定义URLClassLoader,它在运行时在文件系统的其他位置加载一些jar文件。
  2. 实现一个java代理,转换由类加载器加载的类,替换其中一个方法的正文或其他内容。
  3. 在应用程序内部将一个类的实例分配给其接口并调用其检测方法。
  4. 使用-javaagent运行应用程序以进行检查。

1 个答案:

答案 0 :(得分:2)

我假设你的类没有正确检测,因为你正在调用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方法调用上述代理。您还可以禁用类文件格式更改,并在附件期间(可能)加载类时重新定义现有类。