Quick Start • Features • Projects • Contributing • License
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.
If you have usage or general questions please ask them in the Guardsquare Community.
Please use the issue tracker to report actual bugs 🐛, crashes, etc.
ProGuardCORE requires Java 1.8 or higher. You can download it as a pre-built artifact from either
- JCenter or
- Maven Central.
Just add it to your project dependencies, e.g. Gradle:
dependencies {
compile 'com.guardsquare:proguard-core:7.0.1'
}
or Maven:
<dependency>
<groupId>com.guardsquare</groupId>
<artifactId>proguard-core</artifactId>
<version>7.0.1</version>
</dependency>
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
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
- Replacing instruction sequences
- Kotlin metadata
- Code analysis with abstract evaluation
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.
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!
Contributions, issues and feature requests are welcome. Feel free to check the issues page and the contributing guide if you would like to contribute.
Copyright (c) 2002-2020 Guardsquare NV. ProGuardCORE is released under the Apache 2 license.