ReDroid is a toolbox for automatically detecting and countering anti-sandbox behaviors in Android apps. You can find the source here. This post gives a usage example of ReDroid by walking through what ReDroid have done for an Android app equipped with anti-sandbox behaviors, anti-emulator.

Intuition

Apps with anti-sandbox behaviors would behave differently on real and emulated environments. This mechanism enable an app to hide their malicious behavior (for malicious apps) or protect itself from reverse engineering (for commercial apps) in emulators. Take the anti-emulator app as an example, we can run the app on an emulator and a real device:

Emulator      Real Device
 

As we can see from the screenshots, the anti-emulator app contains common anti-sandbox behaviors, including:

  • isTaintTrackingDetected: Checking whether there is TaintDroid installed;
  • isMonkeyDetected: Checking whether Monkey is running;
  • isDebugged: Checking whether the app is being attached by any debugger;
  • isQEmuEnvDetected: Checking whether some critical file related with QEMU emulator is present;
  • slowGraphicDetected: Checking whether the environment has poor graphic performance, which indicates that the platform might be an emulator.

Among these checking items, only isQEmuEnvDetected and slowGraphicDetected is checked out by anti-emulator app. ReDroid’s goal is to automatically eliminate the such differences of app behaviors between the two kinds of platforms. In this post, this is to make the emulator undetectable by anti-emulator.

Default Workflow

ReDroid’s default workflow consists of mainly two phases, detecting and countering phase. In detecting phase, ReDroid collects information about the app automatically by exercising dynamic tests on an app; in countering phase, ReDroid analyzes information collected and forms a plan for countering anti-sandbox techniques detected. In the case of anti-emulator, we would like to counter isQEmuEnvDetected and slowGraphicDetected checks.

The default workflow of ReDroid can be launched as steps specified on source page. After these steps, we can get multiple folders containing information collected and generated by ReDroid, under the specified output_dir folder. Let’s walk through them by the order in which they’re generated.

  1. configs

    Default workflow calls other scripts to do the real job. This folder contains config files generated for those scripts.

  2. ReDroid_apps_droidbot_out

    This folder contains information collected by DroidBot. Before any further action, ReDroid will first launch DroidBot to run dynamic tests on the specified apps with different platforms (emulator and real device). The test cases are the same between platforms, so after that we can detect runtime behavior differences caused by anti-sandbox techniques.

  3. ReDroid_trace_comparator_out

    This folder contains results of trace comparison. ReDroid would then analyzes running traces collected in step 2, and find out suspectable divergences in them. Those divergences are highly likely caused by anti-sandbox techniques. To achieve the goal, ReDroid match the app threads by bipartite graph matching, align the traces and scan through the traces. The following is an example divergence found in anti-emulator:

     {
         "real_trace": [
             "xit .......diff.strazzere.anti.debugger.FindDebugger$tcp.create ([Ljava/lang/String;)Ldiff/strazzere/anti/debugger/FindDebugger$tcp; FindDebugger.java",
             "xit ......diff.strazzere.anti.debugger.FindDebugger.hasAdbInEmulator ()Z FindDebugger.java"
         ]:
         "emu_trace": [
             "xit .......diff.strazzere.anti.debugger.FindDebugger$tcp.create ([Ljava/lang/String;)Ldiff/strazzere/anti/debugger/FindDebugger$tcp; FindDebugger.java",
             "ent .......diff.strazzere.anti.debugger.FindDebugger$tcp.create ([Ljava/lang/String;)Ldiff/strazzere/anti/debugger/FindDebugger$tcp; FindDebugger.java"
         ]
     }
    

    This divergence is caused by the network configuration difference between the two platforms. The code in anti-emulator reads the contents in /proc/net/tcp to findout whether there is a tcp connection, and if you look at the screenshots above again, the real device is out of Internet, which causes the difference. Although the difference is not used in anti-sandbox code directly, this is indeed a difference between real device and “sandbox” and the code finds it out.

  4. ReDroid_dsm/monitor/*

    This folder contains results of trace monitoring. After finding out the diverging point of real device/emulator traces, ReDroid marks all the method calls before the diverging point as suspectable method calls. Suspectable methods have return values that might be vital to anti-sandbox techniques. For example, getDeviceId is used in anti-emulator, which will return IMEI on real devices and null string on emulators.

    ReDroid then monitors the return values of suspectable methods, using JDWP. An example result pair looks like the following:

    1. Real Device

       {
           "thread": 755,
           "eventKind": 42,
           "methodLocation": 38,
           "returnValue": "358239058753973",
           "signature": "(Ljava/lang/String;)Ljava/lang/String;",
           "returnType": "string",
           "classMethodName": "com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId"
       }
      
    2. Emulator

       {
           "thread": 737,
           "eventKind": 42,
           "methodLocation": 38,
           "returnValue": 0,
           "signature": "(Ljava/lang/String;)Ljava/lang/String;",
           "returnType": "object",
           "classMethodName": "com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId"
       }
      

    Given monitoring results like this, ReDroid can give suggestions on countering anti-sandbox techniques.

  5. ReDroid_dsm/dsm/dsm.json

    This file is the DSM rule generated according to results in step 4. DSM (dynamic state modification) is a word borrowed from this paper, which stands for modifying a program’s control flow during runtime. In ReDroid, DSM means changing a method’s return value during runtime, and it’s implemented as a Xposed module.

    Given a dsm rule file, ReDroid Xposed module can make emulator undetectable to the anti-sandbox techniques detected. Here is an example (in getDeviceId) of the rules:

     {
         "stackTrace": [
             "android.telephony.TelephonyManager.getDeviceId",
             "diff.strazzere.anti.emulator.FindEmulator.hasKnownDeviceId",
             "diff.strazzere.anti.MainActivity.isQEmuEnvDetected"
         ],
         "emuReturnType": "object",
         "emuReturnValue": 0,
         "returnValue": "358239058753973",
         "returnType": "string",
         "classMethodName": "android.telephony.TelephonyManager.getDeviceId",
         "paraList": []
     }
    

    stackTrace means the position of this method in calling stack, and paraList is for distinguishing overloading methods. We can see that getDeviceId returns null string for emuReturnValue on emulators and IMEI for returnValue on real devices.

    In anti-emulator’s example, dsm.json finally contains the following suspectable methods:

     [
         "android.os.Parcel.readString",
         "android.telephony.TelephonyManager.getDeviceId",
         "com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId",
         "diff.strazzere.anti.emulator.FindEmulator.hasEmulatorAdb",
         "diff.strazzere.anti.emulator.FindEmulator.hasEmulatorBuild",
         "diff.strazzere.anti.emulator.FindEmulator.hasQEmuFiles",
         "diff.strazzere.anti.MainActivity.isQEmuEnvDetected",
         "java.io.BufferedReader.fillBuf",
         "java.io.File.exists",
         "java.io.FileDescriptor.getInt$",
         "java.io.FileInputStream.read",
         "java.io.InputStreamReader.read",
         "java.lang.ThreadLocal.-get0",
         "libcore.io.BlockGuardOs.read",
         "libcore.io.IoBridge.read",
         "libcore.io.Posix.read"
     ]
    

    Back to the two screenshots at beginning, the isQEmuEnvDetected check item is solved by java.io.File.exists DSM, for anti-emulator uses this method to check related files like /system/lib/libc_malloc_debug_qemu.so. Also the outer function hasQEmuFiles are hacked.

    The dsm.json file will be uploaded to the emulator’s /data/system/ReDroid/ path and loaded by ReDroid Xposed module. After that, the anti-emulator app cannot detect QEMU files in emulators any more:

    As for the slowGraphicDetected check item, it’s not covered in the method set before diverging point. ReDroid are supposed to applied iteratively here to solve the problem.

Conclusion & Future Work

ReDroid can automatically build an undetectable environment from apk files and its different behavior on emulators and real devices. However, there are some obvious drawbacks:

  1. More accurate DSM generation: Current DSM generation provided by ReDroid is highly heuristic, incomplete and unstable;
  2. More advanced DSM types: Currently ReDroid only supports hacking return values of primitive types, plus String type.

If you like ReDroid, please give it a star :)