关于Android平台上短、彩信的拦截,网上已经有不少介绍性的文章。那些文章大多是介绍具体的实现方法,其提供的方法并不一定能够成功拦截。今天我们就来详深入地介绍一下拦截短、彩信的内部原理,并分析一下拦截方法的优劣。
Android接收短、彩信的原理
正常的短信在到达手机后,会根据不同的制式(GSM、CDMA等)被解析并封装成SmsMessage对象。该对象会被SMSDispatcher进一步解析并分发。系统会将短信的“format”、“pdu”放到action为“android.provider.Telephony.SMS_RECEIVED”的Intent中。之后系统会将Intent以有序广播(Orderd Broadcast)的形式发出去。监听这两个广播的app收到广播后,通过对pdu的解析,就可以得到短信的具体内容。
彩信的情况与短信类似,只不过运营商首先会发一条WapPush短信。这类短信不包含彩信的具体内容,而是包含一条指向真实内容的URL地址。系统会将WapPush短信解析,提取“pduType”、“header”、“pdu”、“format”等信息放到action为“android.provider.Telephony.WAP_PUSH_RECEIVED”的Intent中。同样,系统会以有序广播的形式将Intent发出去。收到广播的app将intent解析,并提取其中的URL。之后访问该URL地址并下载实际的彩信内容。
有序广播的原理
经过以上的分析,拦截短、彩信的关键就是拦截action为“android.provider.Telephony.SMS_RECEIVED”和“android.provider.Telephony.WAP_PUSH_RECEIVED”的广播。由于该广播是有序广播,所有Receiver 是按照顺序接受广播的。先收到广播的Receiver有权利终止该广播的传播。因此只要一个Receiver能够第一个收到广播并终止继续传播,那么就可以实现短、彩信的拦截。
这里简单介绍一下有序广播的传播方式。BroadcastReceiver 有静态和动态两种方式注册。
1. 静态注册:
静态注册是指在AndroidManifest.xml 中注册。方法如下:
<receiver android:name=“.XXXMessageReceiver”>
<intent-filter android:priority=“1000”>
<action android:name=“android.provider.Telephony.SMS_RECEIVED” />
</intent-filter>
</receiver>
2. 动态注册:
动态注册是通过调用Context类的register方法来注册。方法如下:
registerReceiver(BroadcastReceiver receiver,
IntentFilter filter);
registerReceiver(BroadcastReceiver receiver,
String broadcastPermission,
Handler scheduler);
注意,动态注册Receiver 的优先级是通过IntentFilter.setPriority(int)方法来设置的。当一个广播发生时,系统会将静态和动态的Receiver 按优先级(priority)进行排序,优先级越高越早处理。对于优先级相同的Receiver ,先处理动态的。
第三方解决方案
网上的通常解决方案是采用静态注册的方式注册BroadcastReceiver ,并设置一个较高的优先级,例如,1000、9999等。经过以上对于广播原理的分析,可以看出,这种方式其实并不能够保证接受者会第一时间被触发。比较好的实现方法需要注意两点:
1. 采用动态方式而非静态方式注册。监听系统开机广播,系统启动之后采用动态方式注册BroadcastReceiver 。这样可以保证在第一时间将需要的BroadcastReceiver注册成功;
2. 并将优先级设为最高。注意,系统的最高优先级不是网上所说的1000、9999,而是2147483647。这是int类型能够表示的最大数值。
做到这两点可以大大增加拦截的成功率。由于第三方app本身的局限性,即使采用优化过的方法,第三方app还是不能够保证拦截的成功率。例如,如果有两个app都采用动态注册的方式,都设置其优先级为最高。那么其中一个就会被另一个拦截。
系统级拦截方案
拦截短、彩信的根本方法还是需要在系统层面来解决。需要在系统提供一个专用的Service(类似于ActivityManagerService、PackageManagerService),用于判断收到的短信是否需要拦截。接口如下:
boolean needToBlockMsg(Intent intent);
系统在收到短信后,将短信内容组装到需要广播的Intent中。然后调用该接口判断是否需要拦截。如果需要拦截,则抛弃该短信,或者将其保存到特定的数据库中;如果不需要拦截,那么就照常发送广播。这样可以一劳永逸地解决短、彩信的拦截问题。
由于需要修改“/system/framework/framework.jar”之类的系统文件,第三方app是无法实现的,因此需要设备生产厂商的支持。
更多内容请关注http://blog.sina.com.cn/u/3194858670以及sina微博@安卓安全小分队