6 minute read

Hello everyone! In this blog post , I will try to analyze the Coper Banking Malware detected by Dr.Web team in July and explain the unpacking stages.

What is Coper malware and why you analyze Coper?

Coper malware is an Android banking trojan discovered by a Dr. Web. This malware is targeting Colombian users and it uses icons similar to Bancolombia mobile apps to look legitimate.

My main reason for analyzing Cooper malware is that I haven’t analyzed the packing method that this malware uses before and I think it’s worth analyzing. Malware have modular architecture, unpack intertwined parts and load the final payload apk like matryoshka :)

Step by Step Manual Unpacking

Coper malware infection process have some unpacking stages. First stage of infection is installing the dropper. The main task of dropper apks is to take the payload/malicious apk from another location or from its own content and load it. Threat actors generally use droppers to spread malware on Google Play.

First of all, lets open up the apk on jadx and check the manifest file.(I renamed the apk file as DROPPER.apk)


Dropper requests some permissions:

  • RECEIVE_BOOT_COMPLETED to run after device reboot.
  • REQUEST_INSTALL_PACKAGES to install malicious packages.
  • WRITE_EXTERNAL_STORAGE to create decrypted file from encrypted malicious files.
  • BIND_ACCESSIBILITY_SERVICE to get Accessibility Service permission and prevent itself from removing.

Also some receivers defined in manifest file to keep track of what’s going on on the infected device.

package com.lookfeetudnl;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;

public class KmMVP extends Activity {
    public static Context astBiauOCY;

    /* access modifiers changed from: protected */
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.rtaxori);
        astBiauOCY = this;
        MwiWiLkkt.fRhiGyOl(this, 0);
    }

    /* access modifiers changed from: protected */
    public void onPause() {
        super.onPause();
        MwiWiLkkt.fRhiGyOl(this, 12);
    }
}

When we read the main activity code, it sets the layout and context, after that it calls MwiWiLkkt.fRhiGyOl function.

package com.lookfeetudnl;

public class MwiWiLkkt {
    public static MwiWiLkkt BIRRPiHgEFEz;

    public native String JYGIvXY(int i);

    public native void bExNRbB(Object obj, Object obj2, int i, Object obj3);

    public native Object lWSU2LArPj();

    public native boolean nw7eMjO(int i);

    public native void qWoKVTa(String str, String str2);

    public native String x599wo6(String str, String str2);

    public static void fRhiGyOl(Object obj, int i) {
        fRhiGyOl(obj, i, null);
    }

    public static void fRhiGyOl(Object obj, int i, Object obj2) {
        if (BIRRPiHgEFEz == null) {
            BIRRPiHgEFEz = new MwiWiLkkt();
        }
        BIRRPiHgEFEz.bExNRbB(KmMVP.astBiauOCY, obj, i, obj2);
    }

    static {
        System.loadLibrary("rWm7eCi");
    }
}

In this class, some native functions defined, “rWm7eCi” native library loaded and bExNRbB function called from native lib. When android apps call functions from native libs, they have to load them into memory and developers can do this with two api call:

System.loadLibrary("xyz");
//or
System.load("lib path like lib/x86/libxyz.so");

In this sample, our library file name is “librWm7eCi.so”. Generally malware developers hide their libs or payloads in another directories like assets,res etc. Coper malware developers puts their encrypted payload file in assets folder. We can extract apk file with apktool and get this files.


We have encrypted payloads with docx and html extension, to understand how they decrypted and loaded lets continue to analyze code.

When we open native lib file with ghidra to analyze it, and decompile the “bExNRbB” function we will see a lot of codes like this:

*param_1 + 0x7c
*param_1 + 0x178
.
.

This address calculations comes from JNIEnv. JNIEnv is a struct of function pointers and this hex values are offsets. Every jnı function have an offset they called with this hex values and JNIEnv * is a first argument of functions defined in native libs. Here is a table for jnı function offsets: Table

But when reading codes,looking at the table on every call is boring and not efficient. Thanks to Ayrx shared this Ghidra Data Types archive we can load this file to ghidra and with this struct we can read the function names :)

  • Download the gdt file from github.
  • Click the arrow button on Data Types Manages section and select “open file archive” and select gdt file.
  • Go to first line of “bExNRbB” function and right click on the first arguments type(int)
  • Select retype and write “JNIEnv *” and click ok.

Lets analyze the code.

The str variable is the path and if dex file is not decrpyted the docx file decrypted as “.xe9clDeM.dex”, its have dot beginning of name to save file as a hidden file.

We found a RC4 key :) Lets decrypt it with cyberchef.

After decryption, dex file loaded with native funtions.

If we dont want to read all this codes and check the dexclassloader is called or not, we can hook function with frida :)

[sh4d0wless@paradise scripts]$ frida -U -f com.lookfeetudnl -l dexclassloader.js --no-pause
     ____
    / _  |   Frida 15.0.8 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
Spawned `com.lookfeetudnl`. Resuming main thread!                       
[Custom Phone 1::com.lookfeetudnl]-> Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex
Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex
Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex
Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex
Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex
Bir dex load gördüm sanki: /data/user/0/com.lookfeetudnl//cache/.xe9clDeM.dex

Loaded dex file have different missions. When the startAcsbMgr function called on native lib its sen arguments to funtion, if we trace the “i” value from dropper apk:

MwiWiLkkt.fRhiGyOl(this, 0); <---- from dropper main function, i=0 

this argument used in “main” function in loaded dex file and different functions from dropper apk send different “i” values to dex funtions. Also loaded dex calls some methods with reflection.

İf the i=8 it calls onstartcommand and attemps to install another apk.

    public void onStartCommand() {
        if (RUNNING) {
            Log.i(TAG, "already started");
            return;
        }
        RUNNING = true;
        boolean z = H.DEBUG;
        if (!installation_started && (!H.isPkgInstalled(H.ctx))) {
            fileCheck();
        }
    }

    public static String JNI_apkUnpack() {
        try {
            return (String) thiz.getClass().getDeclaredMethod("lWSU2LArPj", new Class[0]).invoke(thiz, new Object[0]);
        } catch (Exception e) {
            if (!DEBUG) {
                return "";
            }
            Log.e(TAG, "JNI_apkUnpack: " + e.getMessage());
            e.printStackTrace();
            return "";
        }
    }

To install the apk, it calls “lWSU2LArPj” function from native lib.

This function decrypts “koyA6mUZf1K.html” to “.gZmZnY.apk” file and requests to install apk. The installed apk is responsible for loading the final payload.

To load final payload, it uses native lib again :)

package com.frontbynpxa;

import android.app.Application;
import android.content.Context;

public class SqQBpk extends Application {
    static {
        System.loadLibrary("kJophyBxt");
    }

    public native void XlFQRHBPxR(Object obj);

    /* access modifiers changed from: protected */
    public void attachBaseContext(Context context) {
        super.attachBaseContext(context);
        XlFQRHBPxR(context);
    }
}

Lets decompile the native lib and find the RC4 key.

After decrypting the file on cyberchef, finally we have a final payload :D

Malware Capabilities

I didnt analyze the capabilities of Coper malware but as you can see from unpacking steps malware capabilities are:

  • Overlay attack to target banking apps
  • Prevent removing itself with accesibility permission
  • Intercepting SMS’s
  • Running keylogger
  • Display push notifications
  • Disable Play protect
  • Prevent disabling device administration and more. For more information i certainly recommend reding the Dr.Web analysis.

Video Demonstration

Here is a video of malware infection on genymotion vm.

Video

How to Remove Coper Banking Malware

I didnt try safe mode to remove malware from physical device but probably it works. For genymotion vm we can remove with some adb commands

[sh4d0wless@paradise COPER SAMPLE]$ adb uninstall com.frontbynpxa # second apk
Failure [DELETE_FAILED_DEVICE_POLICY_MANAGER] #cant remove because of device administration 
[sh4d0wless@paradise COPER SAMPLE]$ adb shell pm disable-user com.frontbynpxa #disable device administration
Package com.frontbynpxa new state: disabled-user
[sh4d0wless@paradise COPER SAMPLE]$ adb uninstall com.frontbynpxa
Success # second apk removed :)
[sh4d0wless@paradise COPER SAMPLE]$ adb uninstall com.lookfeetudnl
Success # dropper removed :)

Thanks for reading :) Note: Sorry for my bad English :/

IOC:

References: