ByteBuddy 拦截的方法没有被调用

时间:2021-02-14 03:36:55

标签: java hook byte-buddy

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. 我应该尝试做subclass而不是做redefine吗?如果是这样,如果我 interceptredefine,我可以调用原始函数吗?如果是这样,如何?我用很多不同的方法尝试过这个,结果我的轮子在泥泞中旋转得很厉害。

  3. 我应该做subclass而不是redefinerebase吗?我尝试使用以下代码进行此操作,并从检测库中收到异常:

             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
    

1 个答案:

答案 0 :(得分:1)

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

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

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

相关问题