javabyte-buddy

ByteBuddy agent passing wrong data to ClassReader when using custom class loader


I'm using a Java agent with ByteBuddy to instrument class files. The application I'm instrumenting uses a custom class loader to decrypt classes. I noticed I was hitting this exception when ByteBuddy was attempting to transform classes. After adding some debug prints it seems that the buffer being passed to the ClassReader is the encrypted class file. Is there a way I can make ByteBuddy use the buffer after it has been decrypted by the class loader?

Stack trace:

java.lang.IllegalArgumentException
  at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:263)
  at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:180)
  at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:166)
  at net.bytebuddy.utility.OpenedClassReader.of(OpenedClassReader.java:130)
  at net.bytebuddy.utility.AsmClassReader$Factory$Default$4.make(AsmClassReader.java:134)
  at net.bytebuddy.utility.AsmClassReader$Factory$Default$2.make(AsmClassReader.java:108)
  at net.bytebuddy.utility.AsmClassReader$Factory$Default$1.make(AsmClassReader.java:94)
  at net.bytebuddy.utility.AsmClassReader$Factory$Default.make(AsmClassReader.java:187)
  at net.bytebuddy.pool.TypePool$Default.parse(TypePool.java:911)
  at net.bytebuddy.pool.TypePool$Default.doDescribe(TypePool.java:897)
  at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.access$001(TypePool.java:977)
  at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.doResolve(TypePool.java:1100)
  at net.bytebuddy.pool.TypePool$Default$WithLazyResolution$LazyTypeDescription.delegate(TypePool.java:1169)
  at net.bytebuddy.description.type.TypeDescription$AbstractBase$OfSimpleType$WithDelegation.getTypeVariables(TypeDescription.java:8594)
  at net.bytebuddy.description.type.TypeDescription$AbstractBase.isGenerified(TypeDescription.java:8200)
  at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying.onNonGenericType(TypeDescription.java:1796)
  at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Reifying$1.onNonGenericType(TypeDescription.java:1752)
  at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.accept(TypeDescription.java:3811)
  at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:6363)
  at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.analyzeNullable(MethodGraph.java:729)
  at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.doAnalyze(MethodGraph.java:743)
  at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.compile(MethodGraph.java:668)
  at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$AbstractBase.compile(MethodGraph.java:519)
  at net.bytebuddy.dynamic.scaffold.MethodRegistry$Default.prepare(MethodRegistry.java:472)
  at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.toTypeWriter(RebaseDynamicTypeBuilder.java:230)
  at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4092)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:12725)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12660)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1800(AgentBuilder.java:12369)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:13151)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:13081)
  at java.base/java.security.AccessController.doPrivileged(Native Method)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doPrivileged(AgentBuilder.java)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:12603)
  at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
  at java.instrument/sun.instrument.TransformerManager.transform(Unknown Source)
  at java.instrument/sun.instrument.InstrumentationImpl.transform(Unknown Source)
  at java.base/java.lang.ClassLoader.defineClass1(Native Method)
  at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
  at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
  at net.zk.CustomClassLoader.findClass(CustomClassLoader.java:19)
  at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
  at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
  at net.zk.Loader.main(Loader.java:9)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  at java.base/java.lang.reflect.Method.invoke(Unknown Source)
  at net.zk.Injector.inject(Injector.java:16)
  at java.base/java.lang.Thread.run(Unknown Source)

Solution

  • Byte Buddy loads class files from jars using a ClassFileLocator. You can pass a custom locator to AgentBuilder that implements the decryption.

    The JVM only passes the currently instrumented class file to the agent. If you can limit your instrumentation to not needing other class files, this would also work.