【问题标题】:ByteBuddy intercepted method not getting calledByteBuddy 拦截的方法没有被调用
【发布时间】:2021-05-17 09:45:25
【问题描述】:

private void orsc.ORSCApplet.draw() 绘制视频游戏的每一帧。目标是拦截draw()方法,这样我们就可以自己在当前绘制的帧上绘制,这样我们就可以显示用户通常不可见的信息。

下面我用ByteBuddy新建一个orsc.ORSCApplet的子类,然后拦截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 内。因此,我们将使用类加载器和拦截方法创建一个新的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() 的电话。为什么我的拦截不起作用?我有三个理论:

  1. 尽管使用了myLoader 类加载器,该类仍在其他地方加载。但是,我尝试使用ByteBuddyAgent.install() 并在.load(myLoader, ClassLoadingStrategy.fromInstalledAgent()) 中指定了这一点。

  2. 我应该尝试做redefine,而不是做subclass?如果是这样,如果我intercept 和redefine,我可以调用原始函数吗?如果是这样,怎么做?我尝试了很多不同的方法,结果在泥泞中旋转我的轮子。

  3. 我应该使用rebase,而不是subclass 或redefine?我用下面的代码尝试了这个,并从检测库中收到了一个异常:

             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$2.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
    

【问题讨论】:

    标签: java hook byte-buddy


    【解决方案1】:

    当你创建一个子类时,你必须确保这个子类在它被创建的地方返回,通常来自某种工厂。否则,您的子类将存在,但永远不会被使用。我不清楚您是否在代码中执行此操作。

    Rebasing 可以看作是子类化,其中原始类在其内部进行扩展,以避免这种从代理创建子类通常很困难的限制。但是,这通常需要添加方法或字段,这在已加载类时是不可能的,即使使用 Java 代理也是如此。但是,在您的情况下,如果您避免引用 orsc.ORSCApplet.class,则可以使用 TypePool.Default 创建描述并将其提供给字节好友。如果您随后通过注入加载检测类,它将优先于原始类文件并在您的应用程序中使用。

    然而,最优雅的方法是使用重定义,它可以应用于加载的类。通常,您会为此使用AgentBuilder。如果您想向现有方法添加代码,最好使用Advice,而不是此处的委托,因为建议将代码添加到方法的开头和/或结尾。确保使用DynamicType.Builder::visit 将建议作为装饰应用,而不是常规拦截。

    【讨论】:

    • 嘿 Rafael,感谢您的回答,感谢您制作了如此出色的库!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 1970-01-01
    • 2020-03-29
    • 1970-01-01
    • 2020-08-17
    相关资源
    最近更新 更多