Search code examples
javainstrumentationbyte-buddy

Exception on invocation of java agent instrumented via ByteBuddy


I'm trying to recreate the behavior described in Will's blog post but getting the following exception on an attempt to run it via:

$ java -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityfixer-bootstrap-1.0-SNAPSHOT.jar -jar example/target/securi 
tyfixer-example-1.0-SNAPSHOT.jar                                                                                                                           
Exception in thread "main" java.lang.NoClassDefFoundError: net/bytebuddy/implementation/Implementation$Context$Factory                                     
        at java.lang.Class.getDeclaredMethods0(Native Method)                                                                                             
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)                                                                                       
        at java.lang.Class.getDeclaredMethod(Unknown Source)                                                                                               
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)                                                                       
        at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)                                                                     
Caused by: java.lang.ClassNotFoundException: net.bytebuddy.implementation.Implementation$Context$Factory                                                   
        at java.net.URLClassLoader.findClass(Unknown Source)                                                                                               
        at java.lang.ClassLoader.loadClass(Unknown Source)                                                                                                 
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)                                                                                     
        at java.lang.ClassLoader.loadClass(Unknown Source)                                                                                                 
        ... 5 more                                                                                                                                         
FATAL ERROR in native method: processing of -javaagent failed 

The structure is as described in Will's blog - 3 separate jars, one with the agent, one with the interceptor and one with Main class.

I also tried to run it as an executable jar by adding the mainClass stanza into the securityfixer-example's manifest, but that seems to be bypassing instrumentation altogether:

$ java -jar example/target/securityfixer-example-1.0-SNAPSHOT.jar -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityfixer-bootstrap-1.0-SNAPSHOT.jar 
Security manager is set! 
ATTACK SUCCEEDED: Security manager was reset! 

What could I be missing here? Thanks in advance.


Solution

  • The following setup seems to be working:

    byte-buddy-1.0.0.jar must be inside java-agents-experiments\securityfixer\agent\target along with the genarated securityfixer-agent-1.0-SNAPSHOT.jar as the latter depends on the former. This is achieved by including the following plugin, which performs the copying, in securityfixer-agent/pom.xml:

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>2.8</version>
                    <executions>
                        <execution>
                            <id>copy-dependencies</id>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.directory}</outputDirectory>
                                <includeScope>runtime</includeScope>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
    

    as well as the following reference in the <Boot-Class-Path> to the artifact produced by above stanza:

            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            <Agent-Class>com.excelsiorsoft.securityfixer.agent.SecurityFixerAgent</Agent-Class>
                            <Premain-Class>com.excelsiorsoft.securityfixer.agent.SecurityFixerAgent</Premain-Class>
                            <Boot-Class-Path>byte-buddy-1.0.0.jar</Boot-Class-Path>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
    

    So that unnecessary dependencies (such as securityfixer-bootstrap) are not being copied by the above plugin along with the byte-buddy-1.0.0.jar I needed to change their scope to provided. maven-dependency-plugin seems to skip copying dependencies with that scope to its destination folder.

    To be able to run it as executable jar we need to add the <mainClass> stanza to securityfixer-example/pom.xml:

            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>securityfixer.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
    

    .

    $ java -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityfixer-bootstrap-1.0-SNAPSHOT.jar -jar example/target/securit yfixer-example-1.0-SNAPSHOT.jar
    
    
    Security manager is set!    
    ATTACK FAILED: SecurityManager cannot be reset!
    

    Feel free to comment - perhaps there's a more elegant solution. Thanks!