用Xposed框架拦截Android操作系统的短信接收

来源:互联网 发布:淘宝卖家怎么添加客服 编辑:程序博客网 时间:2024/06/05 04:50

短信接收原理

关于Android操作系统短信的接收和发送流程的文章网上有一大堆,但是真正说得很清楚的不多,这篇blog写得不错。其实要想真正弄懂Android操作系统短信的流程,还是Linus的那句话: Read the fucking source code.呵呵
在Android操作系统中,大部分敏感信息的传递过程都是基于binder机制的,当然SMS也不例外。对于SMS的接收流程的描述从Framework层和Application层这两个层面进行介绍。

  • Framework层
    当短信到达Framework层后,会首先启动RIL中的RILReceiver去接收短信,在RILReceiver中使用LocalSocket去读短信,然后把读到的短信放在一个Parcel对象中,然后调用processResponse(Parcel p)去处理,processResponse()中调用processUnsolicited(Parcel p)处理。
    Android操作系统对应的源码如下:
 class RILReceiver implements Runnable {        byte[] buffer;        RILReceiver() {            buffer = new byte[RIL_MAX_COMMAND_BYTES];        }        @Override        public void        run() {            int retryCount = 0;            try {for (;;) {                LocalSocket s = null;                LocalSocketAddress l;                try {                    s = new LocalSocket();                    l = new LocalSocketAddress(SOCKET_NAME_RIL,                            LocalSocketAddress.Namespace.RESERVED);                    s.connect(l);                } catch (IOException ex){                    try {                        if (s != null) {                            s.close();                        }                    } catch (IOException ex2) {                        //ignore failure to close after failure to connect                    }                    // don't print an error message after the the first time                    // or after the 8th time                    if (retryCount == 8) {                        Rlog.e (RILJ_LOG_TAG,                            "Couldn't find '" + SOCKET_NAME_RIL                            + "' socket after " + retryCount                            + " times, continuing to retry silently");                    } else if (retryCount > 0 && retryCount < 8) {                        Rlog.i (RILJ_LOG_TAG,                            "Couldn't find '" + SOCKET_NAME_RIL                            + "' socket; retrying after timeout");                    }                    try {                        Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);                    } catch (InterruptedException er) {                    }                    retryCount++;                    continue;                }                retryCount = 0;                mSocket = s;                Rlog.i(RILJ_LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket");                int length = 0;                try {                    InputStream is = mSocket.getInputStream();                    for (;;) {                        Parcel p;                        length = readRilMessage(is, buffer);                        if (length < 0) {                            // End-of-stream reached                            break;                        }                        p = Parcel.obtain();                        p.unmarshall(buffer, 0, length);                        p.setDataPosition(0);                        //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes");                        processResponse(p);                        p.recycle();                    }                } catch (java.io.IOException ex) {                    Rlog.i(RILJ_LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed",                          ex);                } catch (Throwable tr) {                    Rlog.e(RILJ_LOG_TAG, "Uncaught exception read length=" + length +                        "Exception:" + tr.toString());                }                Rlog.i(RILJ_LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL                      + "' socket");                setRadioState (RadioState.RADIO_UNAVAILABLE);                try {                    mSocket.close();                } catch (IOException ex) {                }                mSocket = null;                RILRequest.resetSerial();                // Clear request list on close                clearRequestList(RADIO_NOT_AVAILABLE, false);            }} catch (Throwable tr) {                Rlog.e(RILJ_LOG_TAG,"Uncaught exception", tr);            }            /* We're disconnected so we don't know the ril version */            notifyRegistrantsRilConnectionChanged(-1);        }    }
  • Application层
    在App层,PrivilegedSmsReceiver在接收到短信来了的广播之后,由SmsReceiver启动SmsReceiverService来做具体的处理。接收短信的action为SMS_RECEIVED_ACTION,所以调用handleSmsReceived()处理,使用insertMessage()将短信插入数据库,这里首先会判断短信是否为CLASS_0短信,如果是则直接显示,不插入数据库。如果不是则会进行消息的替换或者插入数据库,替换使用了SmsMessaged的isReplace()方法判断,原则是短信协议标识mProtocolIdentifier的判断。如果既不是CLASS_0短信也不需要替换,则将短信插入数据库,然后使用MessagingNotification在StatusBar做一个notification,通知用户短信来了。这里就不附上Android操作系统的相关代码了,感兴趣的,可以自己在Grepcode或者AndroidXRef上自己查看。

编码实现

上面已经弄清楚原理了,研究Android操作系统对应部分的源码,不难找出相应的解决方案。这里选择一种比较简单的hook,用xposed框架进行拦截。当然也是经过多次失败尝试后找到的一种比较有效的方法。思路很简单,就是针对短信接收流程中调用的函数,拦截该函数,获取接收端的信息流,对信息流进行按位异或处理。下面给出核心部分的源码:

package com.example.receiver;import de.robv.android.xposed.IXposedHookLoadPackage;import de.robv.android.xposed.XC_MethodHook;import de.robv.android.xposed.XposedBridge;import de.robv.android.xposed.XposedHelpers;import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;/** * @author Li Jiansong * @date:2015-7-27  上午11:15:48 * @version : * *Server端短信接收端的拦截,经过多次尝试,最终有效的是下面的方案 *拦截SmsMessage的内部类PduParser的getUserDataUCS2方法,该方法返回类型为String *String getUserDataUCS2(int byteCount) * */public class RecvHooker implements IXposedHookLoadPackage{    private static final String TARGET_PACKAGE = "com.android.mms";    @Override    public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {        // TODO Auto-generated method stub        //XposedBridge.log("--------loaded app:"+lpparam.packageName);//      if(!lpparam.packageName.equals("com.android.mms"))//          return;        if (!TARGET_PACKAGE.equals(lpparam.packageName)) {            // XposedBridge.log("SendRawSMSMod: ignoring package: " +            // lpparam.packageName);            return;        }//      /**//       * 拦截SmsMessage的内部类PduParser的getUserData方法,//       * byte[] getUserData(){}//       * 该方法不带参数//       *///      final Class<?> recvClazz=XposedHelpers.findClass("com.android.internal.telephony.gsm"//      +".SmsMessage$PduParser",lpparam.classLoader);//      //      XposedBridge.log("==========开始进入拦截----");//      //      XposedHelpers.findAndHookMethod(recvClazz, "getUserData",//              new XC_MethodHook(){//      //          @Override//          protected void afterHookedMethod(MethodHookParam param)//                  throws Throwable {//              // TODO Auto-generated method stub//              //super.beforeHookedMethod(param);//      //              XposedBridge.log("=========getUserData被调用");//              byte[] recvByteSms=(byte[]) param.getResult();//              String strRecvSms="";//              strRecvSms+=new String(recvByteSms);//          //              //byte[] srtbyte = strRecvSms.getBytes();//              //String lsx="6666666666666666666666666666666666";//              param.setResult(strRecvSms.getBytes());//              //SmsMessage msg=new SmsMessage();//              //              XposedBridge.log("========接收的短信内容为:"+strRecvSms);//              return;//          }//          //          //      });        //XposedBridge.log("-------开始拦截");//      findAndHookMethod("com.android.internal.telephony.gsm.SmsMessage",lpparam.classLoader,//              "getSubmitPdu",String.class,//              String.class, String.class, boolean.class, byte[].class,//              int.class, int.class, int.class, new XC_MethodHook(){//          //          /**//           * 拦截SmsMessage的getSubmitPdu方法,其有5个参数//           * String scAddress,//           * String destinationAddress, //           * String message,//           * boolean statusReportRequested, //           * byte[] header//           * //           *///          //          /**//           * Get an SMS-SUBMIT PDU for a destination address and a message//           *//           * @param scAddress Service Centre address.  Null means use default.//           * @return a <code>SubmitPdu</code> containing the encoded SC//           *         address, if applicable, and the encoded message.//           *         Returns null on encode error.//           *///          @Override//          protected void beforeHookedMethod(MethodHookParam param)//                  throws Throwable {//              // TODO Auto-generated method stub//          //  super.beforeHookedMethod(param);//              XposedBridge.log("getSubmitPdu被调用");//              if(param.args[2]==null){//                  return;//              }//              String message=(String) param.args[2];//              XposedBridge.log("======before:SMS message:"+message);//              SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//              message+=df.format(new Date());//              XposedBridge.log("========after   SMS message:"+message);//              SubmitPdu rawPdu=new SubmitPdu();//              //StringTokenizer stringTokenizer=new StringTokenizer(string, delimiters, returnDelimiters)//              param.setResult(rawPdu);//              XposedBridge.log("=============hook替换成功");//              //              return;//              //          }//      });//      final Class<?> recvClazz=XposedHelpers.findClass("com.android.internal.telephony.gsm"//              +".SmsMessage$PduParser",lpparam.classLoader);        XposedBridge.log("=========开始进入拦截");        XposedHelpers.findAndHookMethod("com.android.internal.telephony.gsm"+".SmsMessage$PduParser",                 lpparam.classLoader,"getUserDataUCS2",int.class,                 new XC_MethodHook(){            /**             * Interprets the user data payload as UCS2 characters, and             * decodes them into a String.             *             * @param byteCount the number of bytes in the user data payload             * @return a String with the decoded characters             */            /**             * 拦截SmsMessage的内部类PduParser的getUserDataUCS2方法,该方法返回类型为String             * String getUserDataUCS2(int byteCount)             *              */            @Override            protected void afterHookedMethod(MethodHookParam param)                    throws Throwable {                // TODO Auto-generated method stub            //  super.afterHookedMethod(param);                try {                    String strMms=(String) param.getResult();                    XposedBridge.log("=========before:"+strMms);                    //String after="666666666666666";                    char[] recvArray=strMms.toCharArray();                    for(int i=0;i<recvArray.length;i++){                        recvArray[i]=(char) (recvArray[i]^20000);                    }                    String enCodeSms=new String(recvArray);                    param.setResult(enCodeSms);                    XposedBridge.log("=========after:"+param.getResult());                    //return;                } catch (Exception e) {                    // TODO Auto-generated catch block                    //e.printStackTrace();                    XposedBridge.log(e);                }            }        });    }}

测试

下面给出一个测试例子,向10086发送一条短信,10086自动给出回应信息。由于回应的信息被拦截处理了,所以显示的是乱码。从后台的日志可以看出完整的原来正常的短信信息。
这里写图片描述
这里写图片描述

1 0
原创粉丝点击