Security Testing
Tomaso Vasella
How to Analyze Mobile Apps
Many apps use sensitive, often personal data or have access to it and communicate with various services on the Internet. Against this background, it is useful and interesting to analyze the functionality and data communication of apps from a security perspective. This article shows a pragmatic introduction to the possibilities of analyzing Android apps.
Reverse engineering generally refers to the technical investigation of a ready-made application or system with the goal of obtaining as much detailed and accurate information as possible about how it works, often with the aim of recovering the source code or a representation of it. Depending on the intention, the focus can also be only on the behavior or communication of an application (see also the articles Introduction to the World of Disassembling and Decompiling and Reverse Engineering).
A large number of useful tools exists for reverse engineering. The use of some of them with regard to the analysis of Android apps is illustrated in the following sections.
For the following considerations, an intentionally insecure Android app from the MSTG Hacking Playground which was created for testing purposes is used. Android Apps exist as APK files (Android Package Kit) and they are normal ZIP archives:
$ file MSTG-Android-Java.apk MSTG-Android-Java.apk: Zip archive data, at least v0.0 to extract, compression method=deflate $ unzip -q MSTG-Android-Java.apk -d MSTG-Android-Java $ ls MSTG-Android-Java AndroidManifest.xml assets classes.dex lib META-INF res resources.arsc
The usual contents of an APK file which are also listed in the above example are:
classes.dex
– File that contains the compiled Dalvik byte code of the app in the Dex file format. This byte code is executed by the Android Runtime or its predecessor Dalvik.resources.arsc
– contains some precompiled resources such as strings, colors or styles.res/
– contains most of the XML resources such as layouts or image files in the Binary XML Format.AndroidManifest.xml
– contains meta information about the app such as its name, version, permissions, etc. This file is also a binary XML file.lib/
– contains compiled native libraries that are specific to the CPU architectures that are supported by the app (*.so files), for example arm64-v8a or x86_64.assets/
– a directory with assets of the app, for example image files, fonts or Java Script.META-INF/
– a directory with APK metadata, contains a list of all files of the APK and their signatures (hashes).In the following sections static analysis of Android apps is discussed.
For unpacking and analyzing APK files, there are various tools with different functionalities. The Android Studio contains an APK Analyzer and therefore is one of the most convenient tools, even though it is rather resource-hungry. The APK analyzer can be started through the menu Build / Analyze APK
.
The content of the APK is unpacked and can be further analyzed. The resources present in the APK as binary files are also unpacked and for example the AndroidManifest.xml
file can be viewed in plain text.
When analyzed via Android Studio, the Dalvik bytecode from the classes.dex
file is automatically decoded and can be viewed directly.
If you want to have a closer look at the code, you can display the bytecode directly by right-clicking:
The byte code is displayed as smali, a human readable representation (disassembler syntax) of the byte code. It is also possible to convert the Dalvik byte code to Java byte code. This often works even if the Android app was originally written in Kotlin. Subsequently, the Java bytecode can be analyzed with a Java decompiler, which allows even more convenient viewing.
As an alternative to using Android Studio, the command line tool Apktool can be used. This tool sometimes achieves even better results, especially when dealing with more complex apps:
$ java -jar apktool_2.6.1.jar d MSTG-Android-Java.apk I: Using Apktool 2.6.1 on MSTG-Android-Java.apk I: Loading resource table... I: Decoding AndroidManifest.xml with resources... I: Loading resource table from file: ~/.local/share/apktool/framework/1.apk I: Regular manifest package... I: Decoding file-resources... I: Decoding values */* XMLs... I: Baksmaling classes.dex... I: Copying assets and libs... I: Copying unknown files... I: Copying original files... $ ls MSTG-Android-Java AndroidManifest.xml apktool.yml assets lib original res smali
This tool converts the Dalvik bytecode directly to smali:
$ head -20 smali/sg/vp/owasp_mobile/OMTG_Android/MyActivity.smali .class public Lsg/vp/owasp_mobile/OMTG_Android/MyActivity; .super Landroidx/appcompat/app/AppCompatActivity; .source "MyActivity.java" # static fields .field public static final EXTRA_MESSAGE:Ljava/lang/String; = "com.mycompany.myfirstapp.MESSAGE" # direct methods .method public constructor <init>()V .locals 0 .line 12 invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V return-void .end method
A big advantage of using the the Apktool is that the decoded files (XML etc.) but also the decoded smali code can be edited and afterwards an APK can be recreated with the edited files.
Another useful tool in the analysis arsenal is dex2jar, which converts the Dex files contained in APKs directly to Java classes. Afterwards, the Java code can be easily decompiled.
$ dex-tools-2.1/d2j-dex2jar.sh MSTG-Android-Java.apk dex2jar MSTG-Android-Java.apk -> ./MSTG-Android-Java-dex2jar.jar
The .jar
file created in this way can then be analyzed with a Java decompiler, in the following example the JD-GUI is used for this purpose.
Similar results can be obtained with the tool jadx, which also converts Dex bytecode to Java.
As an example of a possible approach for static analysis, we will use one of the test cases in the test app mentioned above. This app contains a function for creating an encrypted SQLite database.
The corresponding code is easy to find in the decompiled version:
When looking at the method SQLiteEnc()
it becomes apparent that the secret used for the encryption of the database must be returned from the method stringFromJNI()
. Interesting is the method System.loadLibrary()
which loads a native library compiled for the CPU architecture in use. The method stringFromJNI()
is prefixed with the keyword native
which means that this method is implemented in native code using the JNI (Java Native Interface). Listing the directory lib/x86
shows the following files:
$ ls MSTG-Android-Java/lib/x86 libnative-lib.so libsqlcipher.so $ file libnative-lib.so libnative-lib.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=5feb6239bf0911b46bbdf5ff1bccd29570bbb00f, stripped
The secret we are looking for must therefore be generated by a function of the libnative-lib.so
file. There are various possible approaches for the static analysis of this library. As an illustration a short analysis with the tool Ghidra is shown here. First, a new project must be created via the menu Menu File / New Project
, then the library can be loaded via drag and drop, whereby the defaults are already correct here:
Double click on the imported file opens the code browser, which suggests an analysis of the file. In this example all suggested defaults are accepted.
After that we search for the name of the function we found in the Java code: Search / Program Text
:
The locations of the identified functions are then displayed:
Clicking on the name navigates to the corresponding code location:
If the mouse pointer is hovered over the hex values of the variable puVar1
, it can be seen that this code seems to construct a string from dwords. The dwords are 32 bits long, therefore the instructions EAX + 0×4, EAX + 0×8, etc.:
By right-clicking the hex values can be converted with the following result:
The sequence \0
suggests a string terminated with a null byte and it can be assumed that the wanted string must be read backwards, resulting in the secret we are looking for: S3cr3tString!!!
While this example is interesting and illustrative, one could have simply searched for existing strings in the binary:
$ strings /tmp/MSTG-Android-Java/lib/x86/libnative-lib.so ... [^_] [^_] S3cr3tString!!! cannot allocate __cxa_eh_globals std::__libcpp_tls_set failure in __cxa_get_globals() execute once failure in __cxa_get_globals_fast() ...
Similar functionality is available in Ghidra via Window / Defined Strings
Now that we have seen some methods for static analysis of APKs and their contents, it is useful to also take a look at the possibilities for dynamic analysis, i.e. analyzing the behavior of Android apps at their runtime. Conveniently, with the Android Emulator as part of Android Studio, there is a comprehensive way to emulate Android devices, including the ability to install apps from the Google Play Store.
A new virtual device can be created via Tools / Device Manager / Create Device
where the desired Android version can be chosen as well as a version with or without Playstore. Hint: If you want to easily gain root rights on the virtual device later, you have to choose an Android image without Playstore.
The virtual device is created at ~/.android/avd/<device-name>.avd/
and can be started via the Android Studio or usind the command line. As soon as the virtual device is started, it can be reached with the Android Debug Bridge:
> ~/Android/Sdk/platform-tools/adb devices List of devices attached emulator-5554 device
A variety of different methods and tools are available for the dynamic analysis of Android apps, one of the best known and most powerful is probably Frida sein. In the following section, a convenient method for analyzing the communication behavior of Android apps is shown, a step that is required in practially every analysis. An obvious idea for this is to route the communication of apps or of the entire device through a proxy and thus be able to follow the communication. With the emulator, a proxy can be set very easily via the GUI:
With older Android versions, this worked quite well after the proxy’s certificate is installed in the device’s trust store and as long as the apps do not use certificate pinning. With Android versions from Android Nougat onwards, however, it will quickly become evident that most apps no longer communicate with this setup. One reason for this is that in such versions, the apps no longer trust the certificates installed in the user trust store. One could decompile the app, alter the file network_security_policy.xml
and then recompile the app to get the app to trust the certificate in the user store.
A more convenient method is to install the custom certificates in Android’s system trust store which is located at /system/etc/security/cacerts
. However, this requires obtaining root privileges on the device which is very simple to achieve on virtual devices using the Android Emulator and a system image without Playstore. Conveniently, there is the relatively new tool HTTP Toolkit which makes this very simple. After the tool is unpacked and started, it can be connected directly to a started emulator via “Android device via ADB”.
As long as the system image in use allows root privileges, the following steps are fully automated.
HTTP Toolkit includes a request browser that can be used to easily view the communication. In this example, the MSTG Test App was used again.
Today, many tools, some of them very powerful, are readily available for the analysis of Android apps. Simple static and dynamic analyses can be realized with relatively little effort and without overly complex environments. Although in practice much more difficult cases occur regularly, whether due to code obfuscation or for other reasons, one often gets quite far with simple steps. Even when spending only a limited amount of time on looking under the hood of mobile apps and viewing their basic behaviour, is is often possible to find weaknesses, especially since insecure practices such as usind hard-coded API keys or credentials are unfortunately still rather common in mobile apps.
Our experts will get in contact with you!
Tomaso Vasella
Tomaso Vasella
Tomaso Vasella
Tomaso Vasella
Our experts will get in contact with you!