Byte Buddy Documentation

repository·master·Indexed 27 days ago

https://github.com/raphw/byte-buddy

A runtime code generation and manipulation library for the Java Virtual Machine. It enables the dynamic creation and modification of Java classes without a compiler, facilitating the development of frameworks, proxies, and Java agents. The documentation covers Android-specific class loading strategies, the Byte Buddy Gradle and Maven plugins for bytecode enhancement, and the byte-buddy-fuzz module for coverage-guided fuzzing using Jazzer.

Tokens
7.4K
Snippets
16
Records
46
Agent score
92%

What's inside Byte Buddy

  1. Overview of Byte Buddy Fuzzing Harnesses

    master

    The byte-buddy-fuzz module uses Jazzer to perform coverage-guided fuzzing on two primary security surfaces:

    1. Generation (ClassGenerationFuzzer): Tests Byte Buddy's ability to emit class files. The oracle is the JVM byte code verifier; any VerifyError or ClassFormatError is considered a defect.
    2. Parsing (ClassFileParsingFuzzer): Tests TypePool parsing of untrusted class files (specifically generic-signature parsing). Defects include StackOverflowError, OutOfMemoryError, or hangs.

    Requirements & Build Notes:

    • Requires Java 8 or later.
    • The module is excluded from the default reactor and is only built when using the fuzz Maven profile (-Pfuzz).
  2. Create a Java Agent to redefine existing classes

    master

    Byte Buddy can be used to modify existing classes at runtime via a Java Agent. Use the AgentBuilder.Default API to define which types to target (using ElementMatchers) and how to transform them. The entry point for a Java agent is the premain method, which receives an Instrumentation instance.

    ```java
    public class TimerAgent {
      public static void premain(String arguments, 
                                 Instrumentation instrumentation) {
        new AgentBuilder.Default()
          .type(ElementMatchers.nameEndsWith("Timed"))
          .transform((builder, type, classLoader, module, protectionDomain) -> 
              builder.method(ElementMatchers.any())
                     .intercept(MethodDelegation.to(TimingInterceptor.class))
          ).installOn(instrumentation);
      }
    }

    To run the agent, include the -javaagent:your-agent.jar flag in your JVM startup command.

  3. Implement a custom Byte Buddy Plugin

    master

    To create a custom transformation, implement the Byte Buddy Plugin interface.

    Plugin Discovery:

    • Manual: Specify the plugin's location using Maven artifact coordinates in the transformation configuration.
    • Automatic: If the plugin's JAR file declares its name in META-INF/net.bytebuddy/build.plugins, it can be applied automatically when included as a dependency of the byte-buddy-maven-plugin configuration.

    Constructor Arguments: Plugins can declare a constructor that accepts the following types to receive provided arguments:

    • File: The class file root directory.
    • BuildLogger: A logger for the build process.
    • Logger: A Gradle-specific logger.

    You can also supply arguments explicitly via the plugin configuration in the pom.xml.

  4. Continuous Fuzzing with ClusterFuzzLite

    master

    The repository is configured for ClusterFuzzLite integration via GitHub Actions.

    Key components include:

    • .clusterfuzzlite/Dockerfile: Builds on the OSS-Fuzz base-builder-jvm image.
    • .clusterfuzzlite/build.sh: Compiles the fuzzers, prepares the harness jar, and generates the seed corpus.
    • .github/workflows/cflite_pr.yml: Runs the fuzzers for five minutes on every pull request, failing the PR if a crash is detected.

    Note on Security: The ClusterFuzzLite actions in cflite_pr.yml are pinned to the @v1 tag. To comply with the repository's pinned-action policy, you should pin them to a specific commit SHA.

  5. Create a Byte Buddy compiler plugin project

    master

    A compiler plugin project can be a regular java-library or a com.android.library. Using com.android.library allows the plugin to reference Android SDK classes and other Android libraries.

    To ensure the plugin is recognized by Byte Buddy, you must list your Byte Buddy plugin class names in the following resource file: */META-INF/net.bytebuddy/build.plugins*

  6. Generate a Seed Corpus Locally

    master

    To improve fuzzing convergence, you can generate a seed corpus of real .class files. This populates the replay directory for the parsing harness. Note that these files are git-ignored and not committed to the repository.

    Use the SeedCorpusGenerator via the exec-maven-plugin to generate seeds into the classFileParsing regression directory:

    mvn -Pfuzz -pl byte-buddy-fuzz compile \
      org.codehaus.mojo:exec-maven-plugin:3.1.0:java \
      -Dexec.mainClass=net.bytebuddy.fuzz.SeedCorpusGenerator \
      -Dexec.classpathScope=compile \
      -Dexec.args="$(pwd)/byte-buddy-fuzz/src/test/resources/net/bytebuddy/fuzz/FuzzRegressionTestInputs/classFileParsing"
  7. Run an Open-Ended Fuzzing Campaign Locally

    master

    There are two ways to run a fuzzing campaign locally:

    1. Using the JUnit Harness

    Drive the existing JUnit harness in fuzzing mode. New findings will be written directly to the ...Inputs/<method> directory:

    JAZZER_FUZZ=1 mvn -Pfuzz -pl byte-buddy-fuzz -am test

    2. Using the Standalone Jazzer Driver

    Invoke the Jazzer driver directly against a specific harness class. This allows you to persist coverage in a dedicated corpus directory:

    java -cp "byte-buddy-fuzz/target/classes:$(cat cp.txt):jazzer_standalone.jar" \
      com.code_intelligence.jazzer.Jazzer \
      --target_class=net.bytebuddy.fuzz.ClassFileParsingFuzzer \
      /path/to/corpus \
      -max_total_time=600
  8. Set up the environment for Android plugin tests

    master

    Before running the Android plugin tests, ensure the following environment requirements are met:

    • ANDROID_HOME: This environment variable must be set and point to your Android SDK directory.
    • Java: Java 11 is required.
    • Android Device: An Android device must be connected via ADB. This can be a running Emulator or a physical device with USB debugging enabled.
  9. Install the Byte Buddy Maven Plugin

    master

    To apply bytecode enhancements during your Maven build process, add the byte-buddy-maven-plugin to your pom.xml. You must define an execution for the transform goal and configure one or more transformations. Each transformation requires the Maven coordinates (groupId, artifactId, version) of the plugin containing the Byte Buddy Plugin implementation, and the fully qualified name of the plugin class.

    <build>
      <plugins>
        <plugin>
          <groupId>net.bytebuddy</groupId>
          <artifactId>byte-buddy-maven-plugin</artifactId>
          <version>LATEST</version>
          <executions>
            <execution>
              <goals>
                <goal>transform</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <transformations>
              <transformation>
                <groupId>net.bytebuddy</groupId>
                <artifactId>byte-buddy</artifactId>
                <version>LATEST</version>
                <plugin>net.bytebuddy.build.CachedReturnPlugin</plugin>
              </transformation>
            </transformations>
          </configuration>
        </plugin>
      </plugins>
    </build>
  10. Run Android plugin tests via Gradle

    master

    To run the connected Android tests, you must first build Byte Buddy alongside the Byte Buddy Gradle plugin. Once built, navigate to the root directory of the test project and execute the following command to run the specific ByteBuddyInstrumentedTest class:

    ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=net.bytebuddy.android.test.ByteBuddyInstrumentedTest
  11. Add Byte Buddy compiler plugin to an Android project

    master

    To use Byte Buddy for Android instrumentation, you must follow a two-step process:

    1. Apply the net.bytebuddy.byte-buddy-gradle-plugin to your Android project.
    2. Add your custom compiler project as a dependency using the byteBuddy configuration type.

    Note that byteBuddy dependencies are used at compile time and are not present at runtime. If your instrumentation adds classes that must be available at runtime, you must also include those classes as a regular implementation dependency.

    plugins {
        id 'com.android.application'
        id 'net.bytebuddy.byte-buddy-gradle-plugin' version byteBuddyVersion
    }
    
    dependencies {
        byteBuddy "my.plugin:compiler:1.0.0"
        implementation "my.plugin:library:1.0.0"
    }
  12. Install the Byte Buddy Gradle Plugin

    master

    To apply bytecode enhancements during the build process in a plain Java Gradle project, apply the net.bytebuddy.byte-buddy-gradle-plugin plugin. Note that if the java plugin is not registered, the Byte Buddy plugin remains passive. For Android projects, refer to the specific Android plugin documentation instead.

    plugins {
      id 'java'
      id 'net.bytebuddy.byte-buddy-gradle-plugin' version byteBuddyVersion
    }