Я пытался изменить байт-код нескольких классов, чьи упаковочные файлы jar не находятся в пути к классу - они загружаются с помощью пользовательского ClassLoader
во время выполнения с учетом URL-адреса. Я пытался использовать 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, вызвав toolsation.redifineClasses(), но не знаю, как передать экземпляр инструментария в экземпляр CustomClassLoader; Я новичок в загрузке инструментов/классов и до сих пор не совсем понимаю их механизм. Любая помощь? Спасибо.
Чтобы сделать это просто:
- Внутри приложения создайте собственный URLClassLoader, который во время выполнения загружает некоторые файлы jar в другое место в вашей файловой системе.
- Внедрите агент java, преобразующий класс, загруженный вашим загрузчиком классов, заменив один из его методов или что-то в этом роде.
- Внутри приложения назначьте экземпляр класса его интерфейсу и вызовите его инструментальные методы.
- Запустите приложение с параметром -javaagent для проверки.