如何检测由自定义 class 加载程序加载的 classes?
How to instrument classes loaded by a custom class loader?
我试图修改几个 class 的字节码,它们的打包 jar 文件不在 class 路径中 - 它们在运行时由自定义 ClassLoader
加载,给定一个URL。我尝试使用 java agent
和 ClassFileTransformer
希望拦截那些 classes 但失败了。 classloader 是遗留项目的一部分,因此我无法直接对其进行更改。
代理在 AppClassLoader 'locally' 加载的 classes 上工作正常,但只是忽略自定义 classloader 加载的那些。
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 实例;我是 instrumentation/class 加载的新手,对它们的机制还不是很清楚。
有什么帮助吗?谢谢。
为简单起见:
- 在应用程序内部创建一个自定义 URLClassLoader,它在运行时加载文件系统中其他地方的一些 jar 文件。
- 实施一个 java 代理转换一个 class 由你的 classloader 加载,替换其中一个方法的主体或其他东西。
- 在应用程序内部,将 class 的实例分配给它的接口并调用它的检测方法。
- 运行 带有-java代理的应用程序要检查。
我假设您的 class 没有正确检测,因为您正在调用 ClassPool.getDefault()
,它不包括您的自定义 class 加载器可见的 class 文件,但仅用于系统 class 加载程序。您永远不会注册 classfileBuffer
class 文件。
作为替代方案,您可以尝试 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);
可以从 agentmain
或 premain
方法调用上述代理。您还可以禁用 class 文件格式更改并重新定义现有的 class 以防 class 在附件期间已经(可能)加载。
我试图修改几个 class 的字节码,它们的打包 jar 文件不在 class 路径中 - 它们在运行时由自定义 ClassLoader
加载,给定一个URL。我尝试使用 java agent
和 ClassFileTransformer
希望拦截那些 classes 但失败了。 classloader 是遗留项目的一部分,因此我无法直接对其进行更改。
代理在 AppClassLoader 'locally' 加载的 classes 上工作正常,但只是忽略自定义 classloader 加载的那些。
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 实例;我是 instrumentation/class 加载的新手,对它们的机制还不是很清楚。 有什么帮助吗?谢谢。
为简单起见:
- 在应用程序内部创建一个自定义 URLClassLoader,它在运行时加载文件系统中其他地方的一些 jar 文件。
- 实施一个 java 代理转换一个 class 由你的 classloader 加载,替换其中一个方法的主体或其他东西。
- 在应用程序内部,将 class 的实例分配给它的接口并调用它的检测方法。
- 运行 带有-java代理的应用程序要检查。
我假设您的 class 没有正确检测,因为您正在调用 ClassPool.getDefault()
,它不包括您的自定义 class 加载器可见的 class 文件,但仅用于系统 class 加载程序。您永远不会注册 classfileBuffer
class 文件。
作为替代方案,您可以尝试 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);
可以从 agentmain
或 premain
方法调用上述代理。您还可以禁用 class 文件格式更改并重新定义现有的 class 以防 class 在附件期间已经(可能)加载。