Cedric’s Cruft

  • Blog
Browse: Home > Intercepting Android native library calls

Intercepting Android native library calls

Edit: at the time of writing, not many details could be disclosed as part of a responsible disclosure policy. The application in question was BlackBerry Messenger (com.bbm). BlackBerry did not respond to our findings. The full report is now available (unrevised version, including typos, mistakes, etc.).

As part of a uni project, we’ve been busy reverse engineering an Android application. Amongst the things we investigated were the following:

  • SSL checks, using signature based static analysis of the Java bytecode
  • MitM and decryption/modifying of streams

 
The APK contains quite some calls to native libraries. The developers decided to include some libraries that can be found on every Android system as well, suggesting that they might not trust the ones that are delivered with stock Android, and decided to compile their own version. The application core is mostly contained in a shared object as well, which makes it painful to get a quick overview of what is going on inside. We don’t have sources, so have to rely on static and dynamic analysis of the ARM binary.

During disassembly, we noticed a particular text file being written to the data folder with AES-256-CBC encryption. The encryption relies on a few functions in the OpenSSL EVP library, which provides a high-level interface to cryptographic functions, of which they used EVP_BytesToKey and EVP_EncryptUpdate amongst others. A quick google shows that they based this mostly on an example implementation. How do we capture/intercept those calls and log them?

Our first approach was to attach a remote debugger to the process with GDB (or IDA Pro if you prefer and have loads of money) and put breakpoints on the parts where encryption is being used. This worked, but we wanted a more permanent solution.

Is it possible to use LD_PRELOAD’s on Android? Yes it is, we can preload shared objects on Android just like we can on other Linux systems. (Though you may not want to attempt this on Android apitrace project is a nice example of a project that uses a very similar technique to trace OpenGL, Direct3D, and other graphics APIs.

The great thing about LD_PRELOAD is that it can intercept hardcoded paths in the binary thanks to the load order. We created a shared object for ARM/Android, which we could then preload:

Function override
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <openssl/evp.h>
#include <android/log.h>
 
int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md, const unsigned char *salt, const unsigned char *data, int datal, int count, unsigned char *key,unsigned char *iv)
{
    int (*new_evp_bytestokey)(const EVP_CIPHER *type,const EVP_MD *md, const unsigned char *salt, const unsigned char *data, int datal, int count, unsigned char *key,unsigned char *iv);
    new_evp_bytestokey = dlsym(RTLD_NEXT, "EVP_BytesToKey");
 
    __android_log_print(ANDROID_LOG_DEBUG, "SSLOverride", "Salt: {%d,%d}, Key: %s, Count: %d\n", *(unsigned int*)salt, *(unsigned int*)(salt+4), data, count);
 
    return new_evp_bytestokey(type, md, salt, data, datal, count, key, iv);
}
 
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl)
{
    int (*new_evp_encryptupdate)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
    new_evp_encryptupdate = dlsym(RTLD_NEXT, "EVP_EncryptUpdate");
 
    __android_log_print(ANDROID_LOG_DEBUG, "SSLOverride", "Plaintext: %s \n", in);
 
    return new_evp_encryptupdate(ctx, out, outl, in, inl);
}

This will catch the function calls, print the arguments, then pass the call on to the original function.
Compile the C file with your favorite NDK toolchain and linked with -llog (this one has been compiled with API level 14):

1
2
3
4
$ ../android-toolchain/bin/arm-linux-androideabi-gcc -shared -o libsslover.so sslover.c -I /home/cedric/toolchain/openssl-1.0.1e/include/ -llog
 
$ file libsslover.so
libsslover.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped

We then push the shared object to the Android device and preload it into the Dalvik VM by setting the wrap property for the application. You might need root for some of these commands (or remount with read-write permissions):

1
2
$ adb push libsslover.so /data/libsslover.so
$ setprop wrap.com.xyz.yourapp LD_PRELOAD=/data/libsslover.so

Then start the application as you normally would from the launcher, and watch logcat closely:

adb logcat
1
2
3
4
5
6
$ adb logcat | grep --line-buffered -i SSLOver
 
D/SSLOverride(27373): Salt: {87611,13275}, Key: sUp3r1337h4x0rK3Y, Count: 5
D/SSLOverride(27373): Plaintext: userid cedric:13487965
D/SSLOverride(27373):
D/SSLOverride(27373): Plaintext: pass blaatschaap

We’ve got plaintext being spit out in our terminal right before it gets encrypted!

Reverse engineering the HITB binary 100 CTF challenge
cedric

Uncategorized

Android, ARM, Dalvik, debugging, decompile, disassemble, gdb, LD_PRELOAD, loader, native libraries, shared object, SSL, wrap, Zygote

2 Dec 2013

Recent Posts
  • Adding an external hard drive to the Unifi Cloud Key Gen2 Plus
  • Tracing API calls in Burp with Frida
  • SANS Holiday Hack Challenge 2015 writeup
  • SECCON 2015 – Reverse engineering Android APK 2 – 400 writeup
  • WordPress up to version 4.1.2 Stored XSS vulnerability
Recent Comments
    Archives
    • February 2022
    • January 2017
    • January 2016
    • December 2015
    • April 2015
    • June 2014
    • December 2013
    © 2013-2022 Cedric Van Bockhaven