Skip to main content

N Ways to Unpack Mobile Malware

This article will briefly explain methods behind the mobile malware unpacking. It will be focusing on Anubis since it is the latest trending malware for almost a year now. Actors use dropper applications as their primary method of distribution. Droppers find their ways to Google Play store under generic names thereby infecting devices with Anubis. An example of a such dropper may found in the references. There were at least forty cases in Google Play in the last fall targeting Turkish users. @LukasStefanko’s twitter thread may be helpful to get an overview of such campaigns. Anubis malware already analysed by fellows from the industry in a detailed manner. Therefore readers should find it more valuable to have an article focusing on packer mechanisms of Anubis.

The sample used in this article is available at the references section. I strongly recommend downloading the sample and following through the article. I will be dividing this post into three sections.

Packers in Android Ecosystem

Mobile malwares also make use of packers to hide their malicious payloads from researchers and AV programs. This includes reflection, obfuscation, code-flow flattening and trash codes to make unpacking process stealthy. All mechanisms mentioned are used by the Anubis packer and therefore will be explored in this article.

Loading classes at runtime

Android applications must define their used services, receivers, activity classes in AndroidManifest file to use them. In Anubis samples, it is clear that there are many classes not defined in the Manifest file that are simply present in the source code.

This means that a file with non-defined classes should be loaded into application at run-time. There are two main ways of run-time loading in Android:

From file:

  • dalvik.system.DexFile.loadDex depreciated after API 26
  • dalvik.system.DexClassLoader
  • dalvik.system.PathClassLoader

From memory:

  • dalvik.system.InMemoryDexClassLoader (not common in malwares)

Loading from the file requires a dex/jar file to be present in file system. Anubis unpacks the encrypted data file and then drops the decrypted version. Later on malware proceeds loading decrypted dex into the application. After loading with DexClassLoader, malware removes the decrypted dex file. Tracing the dexClassLoader should make the loading routine clear. Since dexClassLoader is a class of dalvik.system package “dalvik.system.dexClassLoader” should be in the code but it is nowhere to be found.

Reflection

Another useful method when dealing with malware is reflection. Reflection is an important concept in Java which lets you to call methods/classes without knowing about them in compile time. There are several classes/methods for reflection.

  • java.lang.Class.forName
  • java.lang.ClassLoader.loadClass
  • java.lang.reflect.Method
  • java.lang.Class.getMethods

Example usage of forName

cObj = Class.forName("dalvik.system.dexClassLoader");

cObj variable holds the class object of dexClassLoader. This enables program to call methods of any given class. The problem is to find where function calls are made to reflection methods.

Catching packers with Frida

frida is a dynamic instrumentation toolkit supported by nearly every operating system. Frida makes it possible to inject a piece of code to manipulate target program and also to trace program calls. In this case it will be used for tracing which reflection calls are made thereby analysing the threads. When previously mentioned function calls are made, console.log will be called additionally. But before that, let’s take a quick recap on how to setup Frida on android emulator.

Download frida-server suitable with your emulator from:
(e.g Genymotion uses x86 architecture.)
https://github.com/frida/frida/releases.

adb push frida-server /data/local/tmp
adb shell
cd /data/local/tmp
chmod +x frida-server
./frida-server &

Frida tools should be installed in host machine by running
pip install frida-tools

After the setup, we can write a script to hook our target methods. We will start by defining variables for classes of our methods.

var classDef = Java.use('java.lang.Class');
var classLoaderDef = Java.use('java.lang.ClassLoader');
var loadClass = classLoaderDef.loadClass.overload('java.lang.String', 'boolean');
var forName = classDef.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader');
var reflect = Java.use('java.lang.reflect.Method')
var member = Java.use('java.lang.reflect.Member')
var dalvik = Java.use("dalvik.system.DexFile")
var dalvik2 = Java.use("dalvik.system.DexClassLoader")
var dalvik3 = Java.use("dalvik.system.PathClassLoader")
//var dalvik4 = Java.use("dalvik.system.InMemoryDexClassLoader")
var f = Java.use("java.io.File")
var url = Java.use("java.net.URL")
var obj = Java.use("java.lang.Object")
var fo = Java.use("java.io.FileOutputStream")

We will be using this code snippet to change implementation of a method.

class.targetmethod.implementation = function(){
    console.log("[+] targetmethod catched !")
    stackTrace()
    return this.targetmethod()
}

console.log("[+] {x} function catched !") will enable us to see if the function is called. If function takes any parameters such as a string, logging those may become helpful during the analysis. Then we can get more information about the thread we are in. Frida is able to call any android function including getStackTrace(). But that requires a reference to the current thread object. Let’s start by getting instance of the thread class:

var ThreadDef = Java.use('java.lang.Thread');
var ThreadObj = ThreadDef.$new();

ThreadObj holds instance of the Thread class and currentThread() can be used to get thread according to https://developer.android.com/reference/java/lang/Thread.html.
We can now use getStackTrace()and also loop through stackElements to print the call stack.

    function stackTrace() {
        console.log("------------START STACK---------------")
        var stack = ThreadObj.currentThread().getStackTrace();
        for (var i = 0; i < stack.length; i++) {
            console.log(i + " => " + stack[i].toString());
        }
        console.log("------------END STACK---------------");
    }

Printing call stack helps to identify call graph of reflections and unpacking mechanisms. For example dexClassLoader might have created with reflection. But when frida hooks into dexClassLoader and prints the call stack, we can see the functions before dexClassLoader is called. Unpacking routines are called at the very beginning of the application. Therefore frida should be attached as soon as possible to catch the unpacking process. Fortunately -f option in frida enables frida to spawn target app itself. frida accepts scripts with the -l parameter.
frida -U -f appname -l dereflect.js
Then frida waits input from the user to continue. %resume will resume the process. Full script is available at my github repository.
https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/dereflect.js

Output without the stackTrace():
With stackTrace()

Voila.
You can see the functions called before the write method. After tracing these interval functions, you can seeRNlkfTEUX and lqfRafMrGeware called right before them. And turns out they are very important functions used in decryption of the encrypted file which we will come back later on.

How to Defeat Packers

We can divide unpacking methods into two sections. Both ways lead to the decrypted file.

Dynamically

  • By hooking:
    • Intercept file.delete (Java level)
    • Intercept unlink syscall (system level)
  • From memory:
    • Dump the memory with gameguardian
    • Dump the memory with custom tools

Statically:

  • Hands on manual unpacking

Dynamically:
Intercepting methods is the easiest way.

By hooking : Java Level
When I first encountered Anubis and realized it was dropping a file, my first solution was hooking into file.delete function.

Java.perform(function() { 
   var f = Java.use("java.io.File") 
   f.delete.implementation = function(a){ 
       s = this.getAbsolutePath() 
       if(s.includes("jar")){ 
          console.log("[+] Delete catched =>" +this.getAbsolutePath()) 
       } 
       return true 
   } 
}) 

This piece of code always returns true to file.delete function. After intercepting we can pull the dropped jar file. ✔

In addition to this we can automate our job with python bindings of frida and go through the folder that our target files are in. There are generally thousands of apks generated from those c&c servers. Since each of them can have different IP addresses embedded, an automated tool could make our life easier.


By Hooking : System Level
But what if malware uses native code to delete files? We can not always hook at Java level. We need to get deeper. What syscall is responsible for deleting file from file system and in libc?

Unlink function takes one parameter, a pointer to filename. We can hook unlink with the help of findExportByName. Code is taken from https://www.fortinet.com/blog/threat-research/defeating-an-android-packer-with-frida.html but I tweaked little bit so deleted file will be printed.

var unlinkPtr = Module.findExportByName(null, 'unlink');

Interceptor.replace(unlinkPtr, new NativeCallback( function (a){
    console.log("[+] Unlink : " + Memory.readUtf8String(ptr(a)))
    }, 'int', ['pointer']));

Let’s run the script.

We intercepted the unlink call, since our script just replaced code of original function with console.log() , file will not be deleted from the file system. ✔

From Memory:
Even when file is deleted from file system because file was loaded into process, we can get trails of the deleted file from memory of that process. Since Android inherits from Linux, we can use /proc/pid folder to give us information about memory regions of a specified process. Let’s look at our target with cat /proc/pid/maps | grep dex filtering the dex.

We have found the trails of dex files. Now we need to dump these sections.

Dump the Memory with Gameguardian:

First way is by “cheating” 🙂 There is a tool called GameGuardian which is used in game hacking. You can do many interesting things with GameGuardian but we will only use dump mechanism for now.

Let’s start by installing and running the APK. Then launch GameGuardian and select the app name from left upmost button. Select right upmost button and the one underneath it. Now you can see dump memory option in menu. Put the hex codes of regions or select regions by clicking arrow buttons and press save. Yay!
We can pull dumped regions with :
adb pull /storage/emulated/0/packer . ✔
Then you will see 2 files in packer folder.
com.eqrxhpdv.cbunlkwsqtz-dfb5a000-e0080000.bin com.eqrxhpdv.cbunlkwsqtz-maps.txt

When examined with file command it detects our dex file as a data file.
We need to fix it by removing parts do not belong to our file.

Dump the Memory with Custom Tools:
Thanks to @theempire_h we can dump regions of memory of the target app with a C program.
https://github.com/CyberSaxosTiGER/androidDump
Here is how to dump a region with androidDump.

adb push androidDump /data/local/tmp
adb shell
cd /data/local/tmp
chmod +x androidDump
./androidDump appname

It dumps 3 blobs of data. ✔

But after dumping it, file command still do not give us the correct type 🙁 It turns out that we should modify the file a little bit. To find magic byte of dex I wrote this script.

import binascii
import sys
filename = sys.argv[1]
with open(filename, 'rb') as f:
    content = f.read()
h = binascii.hexlify(content).split(b'6465780a')
h.pop(0)
h = b'6465780a' + b''.join(h)
dex = open(sys.argv[1][:-4]+".dex","wb")
dex.write(binascii.a2b_hex(h))
dex.close()

https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/deDex.py
After running our script on the file, we open it.

We found our lost classes 🙂

Statically:
Here is a blog post explaining unpacking process from a different perspective. https://sysopfb.github.io/malware,/reverse-engineering/2018/08/30/Unpacking-Anubis-APK.html
I found rc4 key with the help of stackTrace. But apparently searching for ^ value is a very efficient way to find RC4 routines for Anubis 🙂
To find rc4 key easily in JADX, here is quick tip:

  • search “% length”
  • right click to method you are in, press find Usage
  • bArr2 will be used as rc4 key to decrypt.

Here is our sample’s decryption key as bArr2 . Does it look familiar?

With bArr2, we can decrypt the encrypted file from images folder of the APK. Here is a snippet for decrypting the file with bArr2 . Script takes 2 parameters, bArr2 and the encrypted file. For our case it is mediumcrop. APK files can be simply unzipped to access the content of it. ✔

https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/anubis_manual.py

After decrypting and unzipping, we get our dex.

After extracting the config, there is one more step to get the address of c&c server. Malware gets page source of the telegram address and changes Chinese characters with ASCII letters. It then processes the base64 string. After decoding base64, it uses service to decrypt data that encrypted with rc4 scheme. Here is a snippet for decrypting Chinese chars to c&c addresses.

https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/solve_chinese.py

I managed to decrypt the Anubis payload with Androguard without running the APK in an emulator! After dumping the dex file, my script will find the config class printing the c2 and the encryption key. Config class is in one of the a,b or c or in ooooooooooooo{0,2}o classes in newer versions.
By checking counts of “this” keywords in class source code I managed to decrypt all versions of anubis (lazy :P). Here is output of my script to get c2 and key from an Anubis sample. https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/getc2_imp.py

Conclusion

There are many ways to unpack mobile malware and trace packing mechanisms. We might see dalvik.system.InMemoryDexClassLoader used in the future. If this is used, delete hooks will not be able to catch dropped files because everything will be done in memory 🙂 But dumping memory will catch these methods. Knowing different ways always helps. If you have any question feel free to ask in comment section or through @0xabc0

Cheers.

Special thanks to @godelx0

Links & References

Dropper sample:
3c35f97b9000d55a2854c86eb201bd467702100a314486ff1dbee9774223bf0e
Anubis sample:
e01ed0befbc50eeedcde5b5c07bf8a51ab39c5b20ee6e1f5afe04e161d072f1d
https://codeshare.frida.re/@razaina/get-a-stack-trace-in-your-hook/
https://www.fortinet.com/blog/threat-research/defeating-an-android-packer-with-frida.html
https://medium.com/@fs0c131y/reverse-engineering-of-the-anubis-malware-
part-1-741e12f5a6bd3
All materials:
https://github.com/eybisi/nwaystounpackmobilemalware