Reversing an Android APK using ASM library

A quick guide on how to instrument an Android APK with the help of the ASM library and extract information from it

Date and Time of last update Sat 12 Jun 2021
  

In this article we are going to show how ASM library can be used in order to extract certain information from the class files of an APK. Although this solution is not applicable for all APKs as you might already have guessed, it provides a straight forward and easy way when we know exactly what we are looking for. We are going to start by creating a stand-alone JAR file utilizing the ASM library in order to print out information about the methods invoked within a class file. Then we will define exactly what we are looking for and we are going to see examples of the output of the tool. Finally we will close with a suggestion on how you could iterate over a number of APKs using the tool above in order to extract the information you need.

In our example we are going to be looking for the existence of any usage of Unix Domain Sockets. It is outside of the scope of this article to explain what a UDS is, but feel free to google it if you are interested. All you need to know in order to follow this article, is that in Android it can be used as a way for different applications to communicate to its other(IPC mechanism).

Overview


Create a stand-alone tool utilizing ASM library

For our purposes we are going to use version 8.1 of ASM library which can be found here. The Java code used to create the JAR file is shown below:

package com.erev0s;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.io.FileInputStream;
import static org.objectweb.asm.Opcodes.ASM5;

public class Main {

    public static void main(final String args[]) throws Exception {
        FileInputStream inputFile = new FileInputStream(args[0]);
        ClassReader reader = new ClassReader(inputFile);
        GraphClass gc = new GraphClass();
        reader.accept(gc, 0);
    }
}

class GraphClass extends ClassVisitor {
    public GraphClass() {
        super(ASM5);
    }

    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        GraphMethod newVisitor = new GraphMethod(name);
        return newVisitor;
    }
}


class GraphMethod extends MethodVisitor{
    public String MthName;

    public GraphMethod(String name) {
        super(ASM5);
        MthName = name;
    }

    public void visitMethodInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String descriptor, boolean isInterface) {
        System.out.println(owner + "." + MthName + " ===> " + name);

    }
}


You do not need to know a lot about ASM in order to understand the code. In line 19 we extend the ClassVisitor in order to override the method visitMethod with our own implementation which includes the GraphMethod class defined below in line 31. In line 39 we override again the visitMethodInsn which is the actual method that will print the details we are looking for.

In line 40 you can actually see that we are printing the name of the currently visited method along with the owner method which is the method from which the call to the currently visited method originated from.

The following snippet shows an example Java file which was compiled to a class file and then passed to our stand-alone tool.

public class Main {
  int x = 5;

  public static void main(String[] args) {
    Main myObj = new Main();
    System.out.println(myObj.x);
  }
}


The output of the stand-alone tool was:

java/lang/Object.<init> ===> <init>
Main.main ===> <init>
java/io/PrintStream.main ===> println


As we can see in the output, besides the initializations, the tool printed that the main invoked the println method. As a next step now, we need to be specific on what we are looking for, so it can be filtered eventually from results similar to the one above.


Define patterns that will help identify Unix Domain Sockets

The first step in such a case is to inspect how a developer would create a UDS, so we can figure out what we are going to be looking for. As we do not want to go into too many details about UDS given it is only an example, we can find out that the following two cases can be found:

  1. The LocalSocket class
  2. and the LocalServerSocket class

I have linked their documentation from the official pages in case someone would like to explore it further.

An example output from the stand-alone tool we made above against a class file that includes a LocalSocket is shown below:

java/lang/Object.<init> ===> <init>
android/net/LocalSocket.a ===> shutdownInput
android/net/LocalSocket.a ===> shutdownOutput
android/net/LocalSocket.a ===> close
java/lang/String.a ===> startsWith
android/net/LocalSocketAddress.a ===> <init>
android/net/LocalSocketAddress.a ===> <init>
android/net/LocalSocket.a ===> <init>
android/net/LocalSocket.a ===> connect
android/net/LocalSocket.a ===> setSendBufferSize
android/net/LocalSocket.a ===> setReceiveBufferSize
android/net/LocalSocket.a ===> setSoTimeout
android/net/LocalSocket.b ===> isConnected
android/net/LocalSocket.c ===> getOutputStream
android/net/LocalSocket.d ===> getInputStream

Knowing what it will looks like in the output we are able now to filter the results with more precision.

Considerations

An APK can have thousands of different classes and an attempt to figure out if what we are looking for in a manual fashion can take ages. So after we have already all the details in hand for what we are looking for, we can automate the process by simply extracting the contents of the APK(it is just a zip file after all) and finding all the dex files. After we have the list of dex files we could use the dex2jar tool to convert them to JAR files and from there extract again the .class files from within. This would allow us to create a list of all the extracted class files which we can then iterate over and attempt to figure out if what we are looking for is there.

This is an effective and very simple way to extract information from class files and it has extended capabilities as the ASM library can be used in multiple ways. I am by no means an expert in ASM library but it sure can help out in instrumentation of the code without the need to familiarize with other complex or expensive tools for static analysis.