Skip to content

Guardsquare/proguard-core

Repository files navigation



ProGuardCORE

A library to parse, modify and analyze Java class files.


Quick StartFeaturesProjectsContributingLicense


ProGuardCORE is a free library to read, analyze, modify, and write Java class files. It is the core of the well-known shrinker, optimizer, and obfuscator ProGuard, the ProGuard Assembler and Disassembler, and the Kotlin Metadata Printer.

Typical applications:

  • Read and write class files, including any Kotlin metadata.
  • Search for instruction patterns.
  • Create byte code instrumentation tools.
  • Analyze code with abstract evaluation.
  • Build static code analysis tools.
  • Optimize and obfuscate, like ProGuard itself.
  • ... and so much more!

ProGuard core comes with a short manual and pretty nice API documentation.

❓ Getting Help

Please use the issue tracker to report actual bugs 🐛, crashes, etc.

🚀 Quick Start

ProGuardCORE requires Java 1.8 or higher. You can download it as a pre-built artifact from:

Just add it to your project dependencies, e.g. Gradle:

dependencies {
    compile 'com.guardsquare:proguard-core:9.1.1'
}

or Maven:

<dependency>
    <groupId>com.guardsquare</groupId>
    <artifactId>proguard-core</artifactId>
    <version>9.0.0</version>
</dependency>

Building Locally

You can of course also clone and build this repository manually. Using Gradle, just execute the assemble task in the project root:

gradle clean assemble

✨ Features

The repository contains some sample code in the examples directory. Together with the complete set of manual pages they extensively cover the usage of the library and its features in more detail:

👉👉👉    Manual Pages    👈 👈 👈

Below we'll highlight just a few of them in order to better showcase the capabilities.

Creating classes programmatically (manual)

Using the fluent API, it becomes very easy to programmatically create classes from scratch. The data structures directly correspond to the bytecode specifications and ProGuardCORE can even preverify the code for you.

You can create classes, with fields, methods and sequences of instructions. The following concise code creates the iconic HelloWorld class:

ProgramClass programClass =
    new ClassBuilder(
        VersionConstants.CLASS_VERSION_1_8,
        AccessConstants.PUBLIC,
        "HelloWorld",
        ClassConstants.NAME_JAVA_LANG_OBJECT)
        .addMethod(
            AccessConstants.PUBLIC | AccessConstants.STATIC,
            "main",
            "([Ljava/lang/String;)V",
            50,
            code -> code
                .getstatic("java/lang/System", "out", "Ljava/io/PrintStream;")
                .ldc("Hello, world!")
                .invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V")
                .return_())
        .getProgramClass();

Replacing instruction sequences (manual)

ProGuardCORE has an excellent instruction pattern matching engine, which can also replace matched bytecode instruction sequences.

For example, the snippet below defines an instruction sequence, including a wildcard, and replaces it with an equivalent instruction sequence, in all code attributes of all methods of all classes in a class pool.

Instruction[][] replacements =
{
    ____.putstatic(X)
        .getstatic(X).__(),
    ____.dup()
        .putstatic(X).__()
};
...
programClassPool.classesAccept(
    new AllMethodVisitor(
    new AllAttributeVisitor(
    new PeepholeEditor(branchTargetFinder, codeAttributeEditor,
    new InstructionSequenceReplacer(constants, replacements, branchTargetFinder,
                                    codeAttributeEditor)))));

Kotlin Metadata (manual)

The library makes it easy to read, write and modify the Kotlin metadata that is attached to Java classes. The following example prints all the names of Kotlin functions in the metadata attached to the Java class Foo:

programClassPool.classesAccept(
   new ClassNameFilter("Foo",
   new ReferencedKotlinMetadataVisitor(
   new AllFunctionsVisitor(
       (clazz, container, function) -> System.out.println(function.name)))));

Abstract Evaluation (manual)

ProGuardCORE provides a number of ways to analyze code. One of the most powerful techniques is abstract evaluation (closely related to symbolic execution). It executes the code, but instead of computing with concrete values it can compute with abstract values.

Consider the following getAnswer method:

private static int getAnswer(int a, int b)
{
    return 2 * a + b;
}

The table represents the bytecode instructions at their offsets, and the resulting evolving stacks and local variables. Each value slot has a numerical value and its origin (an instruction offset), presented as origin:value.

Instruction Stack v0 v1
[0] iconst_2 [0:2] P0:i0 P1:i1
[1] iload_0 v0 [0:2][1:i0] P0:i0 P1:i1
[2] imul [2:(2*i0)] P0:i0 P1:i1
[3] iload_1 v1 [2:(2*i0)] [3:i1] P0:i0 P1:i1
[4] iadd [4:((2*i0)+i1)] P0:i0 P1:i1
[5] ireturn P0:i0 P1:i1

At every point ProGuardCORE provides access to the symbolic expressions, or if possible and requested, the concrete results.

⚙️ Projects

Some of the projects using ProGuardCORE:

If you've created your own, make sure to reach out through github _ at _ guardsquare.com and we'll add it to the list!

🤝 Contributing

Contributions, issues and feature requests are welcome. Feel free to check the issues page and the contributing guide if you would like to contribute.

📝 License

Copyright (c) 2002-2022 Guardsquare NV. ProGuardCORE is released under the Apache 2 license.