ByteBuddy 截获的方法未被调用
ByteBuddy intercepted method not getting called
private void orsc.ORSCApplet.draw()
绘制视频游戏的每一帧。目标是拦截 draw()
方法,以便我们可以自己绘制当前绘制的框架,这样我们就可以显示用户通常不可见的信息。
下面我用ByteBuddy新建了一个orsc.ORSCApplet
的subclass,然后拦截draw()
函数,这样它就可以正常调用draw()
也呼叫 public void PaintCallback.paint()
.
ClassLoader myLoader = this.getClass().getClassLoader();
new ByteBuddy().subclass(orsc.ORSCApplet.class)
.method(ElementMatchers.named("draw"))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(PaintCallback.class.getMethod("paint"))))
.make()
.load(myLoader); //I have also tried fromInstalledAgent (BuddyByteAgent.install() ran) and INJECTION
电子游戏的main()
在orsc.OpenRSC
里面。因此,我们将使用带有拦截方法的 class 加载器创建一个新的 orsc.OpenRSC
对象:
Class loadedMyClass = myLoader.loadClass("orsc.OpenRSC");
Constructor constructor = loadedMyClass.getConstructor();
Object myClassObject = constructor.newInstance();
return myClassObject; //later on, we call orsc.OpenRSC.main() with this object.
但是,我没有接到任何打给 PaintCallback.paint()
的电话。为什么我的拦截不起作用?我有三个理论:
尽管使用了 myLoader
class 加载程序,但 class 正在其他地方加载。但是,我尝试使用 ByteBuddyAgent.install()
并在 .load(myLoader, ClassLoadingStrategy.fromInstalledAgent())
.
中指定了它
我应该尝试 redefine
而不是 subclass
吗?如果是这样,如果我intercept
和redefine
,我可以调用原始函数吗?如果是这样,如何?我尝试了很多不同的方法,结果我的轮子在泥里打转了。
而不是subclass
或redefine
,我应该做rebase
吗?我尝试使用以下代码进行此操作,并从检测库收到异常:
new ByteBuddy().rebase(orsc.ORSCApplet.class)
.method(ElementMatchers.named("draw"))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(PaintCallback.class.getMethod("paint"))))
.make()
.load(myLoader,
ClassReloadingStrategy.fromInstalledAgent());
java.lang.IllegalStateException: Error invoking java.lang.instrument.Instrumentation#retransformClasses
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:503)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy.apply(ClassReloadingStrategy.java:568)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:225)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:100)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6292)
at reflector.Reflector.injectCallbacksAndReturnClient(Reflector.java:319)
at reflector.Reflector.createClient(Reflector.java:50)
at bot.Main.main(Main.java:164)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:495)
... 7 more
当您创建一个子class时,您必须确保这个子class在创建它的地方返回,通常是从某种工厂返回。否则,您的 subclass 将存在,但永远不会被使用。我不清楚您在代码中是否这样做。
Rebase 可以看作是 subclassing,其中原始 class 在其自身内扩展,以避免这种从通常很难创建工厂的代理进行 subclassing 的限制.然而,这通常需要添加方法或字段,而当 class 已经加载时这是不可能的,即使使用 Java 代理也是如此。但是,在您的情况下,如果您避免引用 orsc.ORSCApplet.class
,则可以创建一个带有 TypePool.Default
的描述并将其提供给 Byte Buddy。如果您随后通过注入加载经过检测的 class,它将优先于原始 class 文件并在您的应用程序中使用。
然而,最优雅的方法是使用重新定义,它可以应用于加载的 classes。通常,您会为此使用 AgentBuilder
。如果要将代码添加到现有方法,理想情况下,您最好使用 Advice
,而不是此处的委托,因为建议将代码添加到方法的开头 and/or 结尾。确保使用DynamicType.Builder::visit
而不是常规拦截将建议作为装饰应用。
private void orsc.ORSCApplet.draw()
绘制视频游戏的每一帧。目标是拦截 draw()
方法,以便我们可以自己绘制当前绘制的框架,这样我们就可以显示用户通常不可见的信息。
下面我用ByteBuddy新建了一个orsc.ORSCApplet
的subclass,然后拦截draw()
函数,这样它就可以正常调用draw()
也呼叫 public void PaintCallback.paint()
.
ClassLoader myLoader = this.getClass().getClassLoader();
new ByteBuddy().subclass(orsc.ORSCApplet.class)
.method(ElementMatchers.named("draw"))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(PaintCallback.class.getMethod("paint"))))
.make()
.load(myLoader); //I have also tried fromInstalledAgent (BuddyByteAgent.install() ran) and INJECTION
电子游戏的main()
在orsc.OpenRSC
里面。因此,我们将使用带有拦截方法的 class 加载器创建一个新的 orsc.OpenRSC
对象:
Class loadedMyClass = myLoader.loadClass("orsc.OpenRSC");
Constructor constructor = loadedMyClass.getConstructor();
Object myClassObject = constructor.newInstance();
return myClassObject; //later on, we call orsc.OpenRSC.main() with this object.
但是,我没有接到任何打给 PaintCallback.paint()
的电话。为什么我的拦截不起作用?我有三个理论:
尽管使用了
中指定了它myLoader
class 加载程序,但 class 正在其他地方加载。但是,我尝试使用ByteBuddyAgent.install()
并在.load(myLoader, ClassLoadingStrategy.fromInstalledAgent())
.我应该尝试
redefine
而不是subclass
吗?如果是这样,如果我intercept
和redefine
,我可以调用原始函数吗?如果是这样,如何?我尝试了很多不同的方法,结果我的轮子在泥里打转了。而不是
subclass
或redefine
,我应该做rebase
吗?我尝试使用以下代码进行此操作,并从检测库收到异常:new ByteBuddy().rebase(orsc.ORSCApplet.class) .method(ElementMatchers.named("draw")) .intercept(SuperMethodCall.INSTANCE.andThen(MethodCall.invoke(PaintCallback.class.getMethod("paint")))) .make() .load(myLoader, ClassReloadingStrategy.fromInstalledAgent()); java.lang.IllegalStateException: Error invoking java.lang.instrument.Instrumentation#retransformClasses at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:503) at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy.apply(ClassReloadingStrategy.java:568) at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:225) at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:100) at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6292) at reflector.Reflector.injectCallbacksAndReturnClient(Reflector.java:319) at reflector.Reflector.createClient(Reflector.java:50) at bot.Main.main(Main.java:164) Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method) at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:564) at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:495) ... 7 more
当您创建一个子class时,您必须确保这个子class在创建它的地方返回,通常是从某种工厂返回。否则,您的 subclass 将存在,但永远不会被使用。我不清楚您在代码中是否这样做。
Rebase 可以看作是 subclassing,其中原始 class 在其自身内扩展,以避免这种从通常很难创建工厂的代理进行 subclassing 的限制.然而,这通常需要添加方法或字段,而当 class 已经加载时这是不可能的,即使使用 Java 代理也是如此。但是,在您的情况下,如果您避免引用 orsc.ORSCApplet.class
,则可以创建一个带有 TypePool.Default
的描述并将其提供给 Byte Buddy。如果您随后通过注入加载经过检测的 class,它将优先于原始 class 文件并在您的应用程序中使用。
然而,最优雅的方法是使用重新定义,它可以应用于加载的 classes。通常,您会为此使用 AgentBuilder
。如果要将代码添加到现有方法,理想情况下,您最好使用 Advice
,而不是此处的委托,因为建议将代码添加到方法的开头 and/or 结尾。确保使用DynamicType.Builder::visit
而不是常规拦截将建议作为装饰应用。