June 16, 2020

    How to Create Class Files Using the ProGuard Assembler and Disassembler

    Guardsquare’s newest open source project, the ProGuardCORE library, gives developers the opportunity to integrate ProGuard’s powerful code parsing and code analysis capabilities into their own Java bytecode projects. It provides all the functionality needed to read, write, modify and analyze Java class files. 

    In this blog, we take a closer look at one of the projects built on the ProGuardCORE library: ProGuard Assembler and Disassembler. The ProGuard Assembler and Disassembler  is a tool developers can use to convert Java bytecode into a custom assembly format and vice versa. We will use the Assembler that is part of this project to recreate a few common Java classes and constructs, including the advanced `invokedynamic` bytecode instruction.

    Creating a ‘Hello World!’ class

    The first class file we are creating is a ‘Hello World’ class. First, we have to decide which Java version to target. In this example, we’ll  target Java 8. We also have to add the class header:

    version 8;
    public class HelloWorld {
    
    }

    Normally when working with Java bytecode, we would always have to specify the super class, even if this super class is `java.lang.Object`. In this case, we can skip that step since the ProGuard Assembler automatically adds this in if no super class is defined.

    Now we have to add our main method, to make sure `HelloWorld.class` is executable:

    version 8;
    import java.lang.String;
    
    public class HelloWorld {
        public static void main(String[] args) {
            
        }
    }
    
    

    So far, this looks like normal Java source code, apart from the `version` declaration. The similarities are intentional.

    Finally, it is time to start writing the bytecode instructions. Fortunately, a simple "Hello World!" doesn’t take many instructions:

    version 8;
    import java.lang.String;
    import java.lang.System;
    import java.io.PrintStream;
    
    public class HelloWorld {
        public static void main(String[] args) {           // Method argument names are optional.
            getstatic System#PrintStream out               // Get the System.out field and put it on the stack.
            ldc "Hello World!"                             // Put "Hello World!" on the stack.
            invokevirtual PrintStream#void println(String) // Invoke the PrintStream.println method.         return                                         // Return from the method.     } } 

    When we save the file as `HelloWorld.jbc`, we can assemble it using the ProGuard Assembler:

    bin/assemble.sh -injar HelloWorld.jbc -outjar HelloWorld.class

    After executing the class, we are greeted with "Hello World!"

    Creating annotations

    As a second example, we are working on a more complex class, a class that will introduce us to an important Java bytecode concept: attributes. The class we are assembling is an annotation. Creating one manually shows how much code the compiler needs to generate for a simple annotation class.

    Like in our first example, we start by defining the version and the class. The only difference here is the class type: `@interface` instead of `class`:

    version 8;
    
    public @interface MyAnnotation {
    
    }
    
    

    At this point, the ProGuard Assembler takes care of adding the `ACC_INTERFACE` and `ACC_ANNOTATION` flags automatically to the class and makes sure the annotation implements `java.lang.annotation.Annotation`.

    Now, we can add some values to our annotation. We include a boolean value, a char value, a String value, an enum value, and an array value.

    version 8;
    import java.lang.String;
    import java.lang.annotation.RetentionPolicy;
    
    public @interface MyAnnotation {
        public abstract boolean booleanValue() [         AnnotationDefault true;
        ];
    
        public abstract char charValue() [         AnnotationDefault '\n';
        ];
    
        public abstract String stringValue() [         AnnotationDefault "The global reference in mobile application protection";
        ];
    
        public abstract RetentionPolicy enumValue() [         AnnotationDefault RetentionPolicy#SOURCE;
        ];
    
        public abstract String[] arrayValue() [
            AnnotationDefault {
                "Look though be true. Do not give dalliance";
                "Too much the rein. The strongest oaths are straw";
                "To th'fire i'th'blood. Be more abstemious";
                "Or else good night your vow!";
            }
        ];
    }
    
    

    The resulting code looks nothing like Java source code. Let’s take a look at the differences:

    First of all, the methods are explicitly declared `public abstract`, whereas in Java these access modifiers can be left out in interfaces.

    Secondly, instead of using `{ }` for our methods, like we did in the Hello World class, we use `[ ]`.
    This notation defines the attributes of the method. Attributes can be placed on classes, fields, methods, and more. In this case, the attributes placed on the methods are `AnnotationDefault` attributes, which specify the default value of an annotation value. There's a multitude of attributes available for the JVM, some of which can only be placed on classes, or fields, or methods.

    Finally, the methods we defined do not have a body. Because they are `abstract`, they cannot contain code. This is expressed using the `;`, as in Java source code.

    After saving this as `MyAnnotation.jbc`, we can assemble it using the ProGuard Assembler. Although the file cannot be executed, we can take a look at it using a decompiler to make sure it assembled properly.

    Creating enums

    Now we're ready to work on enums, short for enumerated types. While enums can be defined very concisely in Java source code, they are much more complex to define in Java bytecode.

    Again, we start by adding the version and the class definition, using `enum` in this case, to make sure the `ACC_ENUM` flag is set on the class. Every enum should extend the `java.lang.Enum` base class, but the ProGuard Assembler inserts it automatically when this isn’t specified.

    version 8;
    
    public enum MyEnum {
    
    }

    Of course, an enum is useless without its constants: we add three enum constants. We also want the enums to actually represent something, a String value.

    version 8;
    import java.lang.String;
    
    public enum MyEnum {
        public static final enum MyEnum PROGUARD;
        public static final enum MyEnum DEXGUARD;
        public static final enum MyEnum IXGUARD;
    
        private final String value;
    }
    
    

    Enum constants are compiled by the Java compiler to `public static final` fields, with the special `ACC_ENUM` access flag to indicate they are part of an enumerated type.

    Now we need to add some functionality to our class. Let's implement the `values` and `valueOf`’ methods. These methods are normally generated by the compiler (and marked with `ACC_SYNTHETIC`), but in this case, we have to create them manually.

    version 8;
    import java.lang.String;
    import java.lang.Enum;
    import java.lang.Class;
    
    public enum MyEnum {
        public static final enum MyEnum PROGUARD;
        public static final enum MyEnum DEXGUARD;
        public static final enum MyEnum IXGUARD;
        // Contains the enum values.
        private static final synthetic MyEnum[] $VALUES;
    
        private final String value;
    
        // Returns a copy of the enum values array.
        public static synthetic MyEnum[] values() {
            getstatic #MyEnum[] $VALUES           // Get the synthetic values field.
            invokevirtual MyEnum[]#Object clone() // Make a clone of the values.         checkcast MyEnum[]                    // Cast the clone to the proper array type.         areturn                               // Return the enum values.     }     // Returns a constant by name.     public static synthetic MyEnum valueOf(String name) {
            ldc MyEnum                                    // Put the MyEnum class on the stack.
            aload_0                                       // Put the String argument on the stack.
            invokestatic Enum#Enum valueOf(Class, String) // Invoke the Enum utility method for valueOf.         checkcast MyEnum                              // Cast the enum constant to the MyEnum object type.         areturn                                       // Return the MyEnum constant.     } } 

    Now `MyEnum` can return all its constants, or a specific constant by name. We used the special `$VALUES` array field to achieve this. The next step is to actually initialize this array.

    We also add a main method to test the enum:

    version 8;
    import java.lang.String;
    import java.lang.Enum;
    import java.lang.Class;
    import java.lang.System;
    import java.io.PrintStream;
    
    public enum MyEnum {
        public static final enum MyEnum PROGUARD;
        public static final enum MyEnum DEXGUARD;
        public static final enum MyEnum IXGUARD;
        // Contains the enum values.
        private static final synthetic MyEnum[] $VALUES;
    
        private final String value;
    
        // Returns a copy of the enum values array.
        public static synthetic MyEnum[] values() {
            getstatic #MyEnum[] $VALUES           // Get the synthetic values field.
            invokevirtual MyEnum[]#Object clone() // Make a clone of the values.         checkcast MyEnum[]                    // Cast the clone to the proper array type.         areturn                               // Return the enum values.     }     // Returns a constant by name.     public static synthetic MyEnum valueOf(String name) {
            ldc MyEnum                                    // Put the MyEnum class on the stack.
            aload_0                                       // Put the String argument on the stack.
            invokestatic Enum#Enum valueOf(Class, String) // Invoke the Enum utility method for valueOf.         checkcast MyEnum                              // Cast the enum constant to the MyEnum object type.         areturn                                       // Return the MyEnum constant.     }     // Constructor for MyEnum constants.     private void <init>(synthetic String constantName, synthetic int ordinal, String value) {
            aload_0                                     // Put the current instance on the stack.
            aload_1                                     // Put the constant name on the stack.
            iload_2                                     // Put the constant ordinal on the stack.
            invokespecial Enum#void <init>(String, int) // Invoke the superclass constructor.
            aload_0                                     // Put the current instance on the stack.
            aload_3                                     // Put the actual String constant value on the stack.
            putfield #String value                      // Store the constant value in the field.
            return                                      // Return from the method.
        }
    
        // Static class initializer, initializes the $VALUES array.
        synthetic static {
            new MyEnum                                      // Create new uninitialized MyEnum instance.
            dup                                             // Duplicate MyEnum instance on the stack.
            ldc "PROGUARD"                                  // PROGUARD constant name.
            iconst_0                                        // PROGUARD constant ordinal.
            ldc "ProGuard"                                  // PROGUARD constant value.
            invokespecial #void <init>(String, int, String) // Invoke constructor for PROGUARD.
            putstatic #MyEnum PROGUARD                      // Store the instance in PROGUARD field.
            new MyEnum                                      // Create new uninitialized MyEnum instance.
            dup                                             // Duplicate MyEnum instance on the stack.
            ldc "DEXGUARD"                                  // DEXGUARD constant name.
            iconst_1                                        // DEXGUARD constant ordinal.
            ldc "DexGuard"                                  // DEXGUARD constant value.
            invokespecial #void <init>(String, int, String) // Invoke constructor for DEXGUARD.
            putstatic #MyEnum DEXGUARD                      // Store the instance in DEXGUARD field.
            new MyEnum                                      // Create new uninitialized MyEnum instance.
            dup                                             // Duplicate MyEnum instance on the stack.
            ldc "IXGUARD"                                   // IXGUARD constant name.
            iconst_2                                        // IXGUARD constant ordinal.
            ldc "iXGuard"                                   // IXGUARD constant value.
            invokespecial #void <init>(String, int, String) // Invoke constructor for IXGUARD.
            putstatic #MyEnum IXGUARD                       // Store the instance in IXGUARD field.
            iconst_3                                        // Put array length for $VALUES on the stack.
            anewarray MyEnum                                // Create a new array with the array length.
            dup                                             // Duplicate array instance on the stack.
            iconst_0                                        // Array index for PROGUARD.
            getstatic #MyEnum PROGUARD                      // Get PROGUARD enum constant field.
            aastore                                         // Store PROGUARD enum constant at 0 in array.
            dup                                             // Duplicate array instance on the stack.
            iconst_1                                        // Array index for DEXGUARD.
            getstatic #MyEnum DEXGUARD                      // Get DEXGUARD enum constant field.
            aastore                                         // Store DEXGUARD enum constant at 1 in array.
            dup                                             // Duplicate array instance on the stack.
            iconst_2                                        // Array index for IXGUARD.
            getstatic #MyEnum IXGUARD                       // Get IXGUARD enum constant field.
            aastore                                         // Store IXGUARD enum constant at 2 in array.
            putstatic #MyEnum[] $VALUES                     // Store the values array in $VALUES field.
            return                                          // Return from method.
        }
    
        public static void main(String[] args) {
            getstatic System#PrintStream out               // Get the System.out field and put it on the stack.
            dup                                            // Duplicate the System.out instance on the stack.
            getstatic #MyEnum PROGUARD                     // Get the PROGUARD enum constant field.
            getfield #String value                         // Get the PROGUARD value field.
            invokevirtual PrintStream#void println(String) // Invoke the PrintStream.println method.         dup                                            // Duplicate the System.out instance on the stack.         getstatic #MyEnum DEXGUARD                     // Get the DEXGUARD enum constant field.         getfield #String value                         // Get the DEXGUARD value field.         invokevirtual PrintStream#void println(String) // Invoke the PrintStream.println method.         getstatic #MyEnum IXGUARD                      // Get the IXGUARD enum constant field.         getfield #String value                         // Get the IXGUARD value field.         invokevirtual PrintStream#void println(String) // Invoke the PrintStream.println method.         return                                         // Return from the method.     } } 

    We assemble the file after saving it to `MyEnum.jbc`, and upon execution, the output shows "ProGuard", "DexGuard", and "iXGuard."

    Using invokedynamic and dynamic constants

    In our last example, we take a look at how to use the `invokedynamic` instruction, added in Java 7, and dynamic constants, added in Java 11. As you probably know, `invokedynamic` is used to implement lambda statements in Java, but we have to go even deeper and create our own bootstrap methods, call sites, and method handles.

    Again, we start by defining a new class, called `Indy`. To make sure we can use the dynamic constants, the class version will be 11, this time.

    version 11;
    
    public class Indy {
    
    }
    
    

    Next, we have to include the bootstrap methods. We are using four different bootstrap methods: two for dynamic constants and two for `invokedynamic` instructions.

    version 11;
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles$Lookup;
    import java.lang.String;
    import java.lang.Class;
    import java.lang.invoke.CallSite;
    import java.lang.invoke.MethodType;
    
    public class Indy [     BootstrapMethods {
            invokestatic #MethodHandle bootstrap0(MethodHandles$Lookup, String, Class) {}
            invokestatic #MethodHandle bootstrap1(MethodHandles$Lookup, String, Class) {}
            invokestatic #CallSite bootstrap2(MethodHandles$Lookup, String, MethodType) {}
            invokestatic #CallSite bootstrap3(MethodHandles$Lookup, String, MethodType) {}
        }
    ] {
    
    }
    
    

    Now, we declare these bootstrap methods in the `Indy` class:

    version 11;
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles$Lookup;
    import java.lang.String;
    import java.lang.Class;
    import java.lang.invoke.CallSite;
    import java.lang.invoke.MethodType;
    import java.lang.invoke.ConstantCallSite;
    import java.io.PrintStream;
    
    public class Indy [     BootstrapMethods {
            invokestatic #MethodHandle bootstrap0(MethodHandles$Lookup, String, Class) {}
            invokestatic #MethodHandle bootstrap1(MethodHandles$Lookup, String, Class) {}
            invokestatic #CallSite bootstrap2(MethodHandles$Lookup, String, MethodType) {}
            invokestatic #CallSite bootstrap3(MethodHandles$Lookup, String, MethodType) {}
        }
    ] {
        public static final String PROGUARD = "ProGuard is the most popular optimizer for Java bytecode.";
    
        // The bootstrap method to get the PROGUARD field MethodHandle.
        public static MethodHandle bootstrap0(MethodHandles$Lookup, String, Class) {
            ldc (MethodHandle) getstatic #String PROGUARD
            areturn
        }
    
        // The bootstrap method to get the PrintStream#println method MethodHandle.
        public static MethodHandle bootstrap1(MethodHandles$Lookup, String, Class) {
            ldc (MethodHandle) invokevirtual PrintStream#void println(String)         areturn     }     // The bootstrap method to get the PROGUARD field dynamically.     public static CallSite bootstrap2(MethodHandles$Lookup, String, MethodType) {
            new ConstantCallSite         dup         ldc (Dynamic) 0 MethodHandle methodHandle         invokespecial ConstantCallSite#void <init>(MethodHandle)         areturn     }     // The bootstrap method to invoke the PrintStream#println method dynamically.     public static CallSite bootstrap3(MethodHandles$Lookup, String, MethodType) {
            new ConstantCallSite         dup         ldc (Dynamic) 1 MethodHandle methodHandle         invokespecial ConstantCallSite#void <init>(MethodHandle)         areturn     } } 

    Finally, we add the main method to make use of the `invokedynamic` bootstrap methods:

    version 11;
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles$Lookup;
    import java.lang.String;
    import java.lang.Class;
    import java.lang.invoke.CallSite;
    import java.lang.invoke.MethodType;
    import java.lang.invoke.ConstantCallSite;
    import java.io.PrintStream;
    import java.lang.System;
    
    public class Indy [     BootstrapMethods {
            invokestatic #MethodHandle bootstrap0(MethodHandles$Lookup, String, Class) {}
            invokestatic #MethodHandle bootstrap1(MethodHandles$Lookup, String, Class) {}
            invokestatic #CallSite bootstrap2(MethodHandles$Lookup, String, MethodType) {}
            invokestatic #CallSite bootstrap3(MethodHandles$Lookup, String, MethodType) {}
        }
    ] {
        public static final String PROGUARD = "ProGuard is the most popular optimizer for Java bytecode.";
    
        // The bootstrap method to get the PROGUARD field MethodHandle.
        public static MethodHandle bootstrap0(MethodHandles$Lookup, String, Class) {
            ldc (MethodHandle) getstatic #String PROGUARD
            areturn
        }
    
        // The bootstrap method to get the PrintStream#println method MethodHandle.
        public static MethodHandle bootstrap1(MethodHandles$Lookup, String, Class) {
            ldc (MethodHandle) invokevirtual PrintStream#void println(String)         areturn     }     // The bootstrap method to get the PROGUARD field dynamically.     public static CallSite bootstrap2(MethodHandles$Lookup, String, MethodType) {
            new ConstantCallSite         dup         ldc (Dynamic) 0 MethodHandle methodHandle         invokespecial ConstantCallSite#void <init>(MethodHandle)         areturn     }     // The bootstrap method to invoke the PrintStream#println method dynamically.     public static CallSite bootstrap3(MethodHandles$Lookup, String, MethodType) {
            new ConstantCallSite         dup         ldc (Dynamic) 1 MethodHandle methodHandle         invokespecial ConstantCallSite#void <init>(MethodHandle)         areturn     }     public static void main(String[] args) {
            getstatic System#PrintStream out
            invokedynamic 2 String $()
            invokedynamic 3 void $(PrintStream, String)
            return
        }
    }
    

    Conclusion

    The ProGuard Assembler is an easy but powerful way for developers to write and modify Java class files and Java bytecode. Combined with the ProGuard Disassembler, the Assembler allows anyone to modify class files directly, without access to the original source files. 

    Discover how Guardsquare provides industry-leading protection for mobile apps.

    Request Pricing

    Other posts you might be interested in