Sunday, January 29, 2017

Reverse Engineering Android APKs

Although I've never actually written any Android app, I've been playing around with its internals a bit. I own a phone that has CyanogenOS by default (that's already history), of course I've rooted it as well as bricking my previous phone during ROM changes.

I also tried reversing some android apps with various degrees of success. My main project was 'hacking' the Xiaomi YeeLight bedside lamp app to be able to control it programatically. Xiaomi did not provide any API but if I can modify the APK to accept commands, that's all I need.

Here are the slides for a talk I gave about basic reverse engineering in Codeaholics Hong Kong meeting. After that you can find some more details about the YeeLight case.

Roadblocks

  • obfuscation (great against getting a general view but not if I'm targeting one specific thing)
  • anti-decompilers (can be always bypassed)
  • anti-debuggers (can also be bypassed)
  • time investment (can not be bypassed)

.apk contents

  • Java code compiled to smali register VM, saved all in classes.dex
  • AndroidManifest.xml in some kind of binary form
  • native machine libraries .so (ARM, x86, ..)
  • resources

smali

  • Icelandic "assembler"
  • register based, as opposed to standard JVM stack-based
  • closer to the CPU, less work for JIT compiler
  • reasonably readable
    const-string v5, "UTF8"
    invoke-static {p0, v3, v4, v5}, Lcom/google/zxing/client/result/optional/NDEFURIResultParser;->bytesToString([BIILjava/lang/String;)Ljava/lang/String;
    move-result-object v2

decompiled

  • no variable names (unless debug symbols)
  • try/catch blocks often broken
  • usually can't use Java compiler to put it back together
  • obfuscation -> all methods and classes are now named alphabetically (cd.i(a, b, c, d))

BCV front-end

  • Makes it easy to run decompilers on .dex or .jar
  • still not quite there for more in-depth analysis
  • so I use ... a text editor!
  • decompile everything to .java, put in git and write comments

Patching APKs

  • Example: YeeLight
  • write a new class in Android Studio (add YeeLight.jar to project)
  • compile to .smali
  • add the smali to already extracted apk folder/smali
  • modify .smali files to construct and invoke the new class

  • rebuild using apktool
  • sign
  • zipalign
  • Install on your device!

Working on the YeeLight app

This app is obfuscated and, quite honestly, contains a lot of code. It has a screen with a colour gradient where touching the colour would change the light color accordingly. I started by finding this Activity and trying to find the click handler. I planned to go deeper and eventually end up in the code that's sending Bluetooth commands but I got lost.

Then I tried to watch the logcat while using the app and found that the colour changes are being echoed in the log. One code search for this particular string got me into a class that was fully obfuscated but probably was somewhere on the way to sending the commands. Further reading the decompiled code revealed a consumer for these messages as well as conversion from a colour object to the Bluetooth message.

The next step was to write a network listener class in Java. It would run in its own thread and accept UDP packets sent to the broadcast address. Each colour change requires only 4 bytes of data so UDP is the simplest choice. Broadcast address is used to avoid needing any configuration - I can just send it out on my home network.

This Java code now needs to be converted to a .smali file. There are tools that should be able to convert it directly from a .class or a .jar but at that time, they did not work. So I ended up creating a dummy Android project in Android Studio to achieve the same result:

  1. Create a project in Android Studio.
  2. Convert classes.dex from the YeeLight apk into a YeeLight.jar using dex2jar.
  3. Add the YeeLight.jar to the project as dependency. This will allow you to call methods from the original APK.
  4. Build APK from the project.
  5. Use apktool to disassemble the result, obtaining a .smali file for your class.

Now you can add this new .smali file to the original APK. You also need to actually create an instance and call this new code in an appropriate place. That requires manually editing the existing .smali code of the app. If you can find where, it's not too difficult.

Finally, rebuild the APK using apktool, zip-align and sign it. This process is a bit more complicated than it should be so I have a little script for it right here: My Apk Scripts

Now you can install the app and try it out. If it works, you may want to disable updates for it otherwise the Play store will overwrite your efforts.

With a custom plugin for Kodi that sends the colour commands over UDP, the result is this:

List of resources and tools