Obfuscating Android Applications using O-LLVM and the NDK
Obfuscation is a technique employed to hide the intent of an application. The techniques used to obscure the intent of an application can vary widely. The most effective techniques can increase the effort of reverse engineering and hinder cracking, and theft of intellectual property. Many applications exist that allow you to obfuscate your Android applications. A few are as follows:
The level at which these obfuscators operates varies. The Android compilation process is roughly Java Code(.java) -> Java Bytecode(.class) -> Dalvik Bytecode(classes.dex). Some obfuscators operate directly on the source files transforming them before compilation, some operate on the Java bytecode, and otheres operate on the Dalvik bytecode.
The Android NDK allows programmers to sidestep the VM to squeeze performance or interact more directly with the kernel and hardware. Google's description of the NDK is as follows: "The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can be helpful so you can reuse existing code libraries written in these languages, but most apps do not need the Android NDK."
Android applications that have components that target native machine code as opposed to the DalvikVM do not have as many obfuscation options. One notable exception is the Obfuscator-LLVM project. This is a particularly interesting project because it targets the LLVM. This makes the tool extremely portable as it can apply to as many languages as LLVM has front-ends for and work on as many CPU archtectures as there are LLVM back-ends. 0vercl0k released a paper before the release of o-llvm explaining that details some of the advantages of working with LLVM as well as a simple code transform.
I had set up and used O-LLVM + NDK some time ago. I decided to write a blog post about it after I saw TowelRoot using O-LLVM. Towelroot is a Android privledge escalation that exploits a Linux kernel bug in the futext syscall. A writeup about the exploit can be found here. O-LLVM was presumably used here so that others could not copy and use the exploit for malicious purposes or to repack it and sell it under a different name.
The instructions, as follows, should help you get up and running to allow you to obfuscate your native bits as seen on TowelRoot.
Using NDK O-LLVM binary overlay
I've compiled the obfustor as a binary overlay on top the NDK for both OSX and Linux. You can also perform this step manually by building from source (see the last section). The obfuscator binary overlays are here:
Download the appropriate binary overlay, natigate to your NDK directory and extract using tar xvf <Obfuscator.tar.bz2>
where
Setting up an O-LLVM NDK project
Now, we want to configure our Android NDK project to use our obfuscator. The layout of our project will look like:
➜ AndroidObfuscation-NDK git:(master) tree .
.
├── jni
│ ├── Android.mk
│ ├── Application.mk
│ └── obfuscationTest.c
In your Application.mk of your project,
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
APP_ABI := armeabi
NDK_TOOLCHAIN_VERSION := clang3.4-obfuscator
include $(BUILD_EXECUTABLE)
Information about the various transforms can be found here: Obfuscator Wiki. The way to pass those flags through to the obfuscator is via the LOCAL_CFLAGS
directive. The obfuscator flags need to be prefixed with -mllvm so that clang will pass them through.
An example Android.mk would look like:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := obfuscated
LOCAL_SRC_FILES := obfuscationTest.c
LOCAL_LDLIBS := -static
LOCAL_CFLAGS := -mllvm -sub -mllvm -fla -mllvm -bcf
include $(BUILD_EXECUTABLE)
Now, we can build the project:
➜ AndroidObfuscation-NDK git:(master) ndk-build
[armeabi] Compile thumb : obfuscated <= obfuscationTest.c
[armeabi] Executable : obfuscated
[armeabi] Install : obfuscated => libs/armeabi/obfuscated
An example project can be found here using the above setup and structure: https://github.com/Fuzion24/AndroidObfuscation-NDK.
The binary overlay I prebuilt above also contains the experimental string obfuscation techniques built by yag00. You can enable the string obfuscation by passing the "-mllvm -xse" flag through the LOCAL_CFLAGS directive.
➜ AndroidObfuscation-NDK git:(master) cat jni/obfuscationTest.c
#include <stdio.h>
int main(void){
printf("Hello, world\n");
return 0;
}
Before using the string obfuscation pass:
➜ AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep Hello
Hello, world
After using the string obfuscation pass:
➜ AndroidObfuscation-NDK git:(master) strings libs/armeabi/obfuscated | grep Hello
(Optional) Building O-LLVM from source for the NDK
git clone -b llvm-3.4 https://github.com/obfuscator-llvm/obfuscator.git
cd obfuscator
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE:String=Release ../obfuscator/
make -j5
Full instructions for building o-llvm are here, but the above instructions should suffice.
cp -r $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4 $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator
Open $NDK_PATH/toolchains/arm-linux-androideabi-clang3.4-obfuscator/setup.mk
and change
TARGET_CC := $(LLVM_TOOLCHAIN_PREFIX)clang$(HOST_EXEEXT)
TARGET_CXX := $(LLVM_TOOLCHAIN_PREFIX)clang++$(HOST_EXEEXT)
to (making sure to change
LLVM_TOOLCHAIN_PATH := <PATH_TO_OBFUSCATOR_REPO>/build/bin/
TARGET_CC := $(LLVM_TOOLCHAIN_PATH)clang$(HOST_EXEEXT)
TARGET_CXX := $(LLVM_TOOLCHAIN_PATH)clang++$(HOST_EXEEXT)