用Xposed架構攔截Android作業系統的簡訊接收

來源:互聯網
上載者:User

標籤:xposed架構   sms接收流程   android   

簡訊接收原理

關於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自動給出回應資訊。由於回應的資訊被攔截處理了,所以顯示的是亂碼。從背景日誌可以看出完整的原來正常的簡訊資訊。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

用Xposed架構攔截Android作業系統的簡訊接收

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.