apsun / NekoSMS

A pattern-based text message blocker for Android.
GNU General Public License v3.0
417 stars 44 forks source link

拦截的SIM卡2的短信恢复后,显示为SIM卡1(SubId未能正确记录,均为1) #71

Closed Hovn closed 4 years ago

Hovn commented 4 years ago

作者你好: 一直很喜欢您这个项目,帮助我拦截了大量的垃圾短信。

问题描述: 1、双SIM卡手机,某条发送给SIM2的短信被NekoSMS拦截后,对该条短信进行恢复,在默认短信APP显示是由SIM1进行接收的。 2、检查NekoSMS的数据库,发现 sub_id 全是 1,猜该字段用于标识接收的SIM卡,修改该字段=2后进行恢复测试,短信APP即显示SIM2接收。

问题原因: 基于对项目的喜爱及学习的态度,尝试对问题进行定位。查看项目代码和涉及的Android代码,以及自行编译测试后,对问题原因描述如下:

NekoSMS对 InboundSmsHandler.javadispatchIntent()方法用beforeHookedMethod()进行了拦截。拦截的入参有个intent,而subid是由方法体中代码(如下)赋值到intent中的, SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); 故拦截的intent未能包含正确的subid(因为subid赋值还未执行),而根据intent获得的message的subid也就不正确。后续再通过反射(Integer)ReflectionUtils.invoke(sGetSubId, message);去取的subid也就不正确。

关键点: 1、dispatchIntent()方法执行前已被拦截( beforeHookedMethod()) 2、分析7.1.1源码发现subid是在dispatchIntent()执行后才有的 Android源码:http://androidxref.com/7.1.1_r6/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java#1023

修复方法(仅测试了本人手机,供参考): beforeHookedMethod()的时候就要对intent或message设置正确的subid。

phoneId=param.thisObject.mPhone.getPhoneId(); //通过反射获取,mPhone可能在父类中 subId=phoneId+1; //实测得出,具体关系未知。

intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);message.setSubId(subId);

最后: 才疏学浅,以上还请开发者检查确认~


If you are submitting a bug report and do not include the following info, your issue will be ignored!

Please paste your Xposed logs (Xposed Installer -> Logs -> Menu -> Save to SD card) below:

无对此问题可用的xposed log 

Thank you for helping us help you help us all.

apsun commented 4 years ago

Awesome find and bug report! Looks like the subscription id code never actually worked (I never had a dual SIM phone with Xposed so I never tested it). As for the subId = phoneId+1, we should just call the same API that dispatchIntent uses, which looks like SubscriptionManager.putPhoneIdAndSubIdExtra.

Hovn commented 4 years ago

感谢指正。解决方法只需在hook dispatchIntent() 时原样执行原方法中的SubscriptionManager.putPhoneIdAndSubIdExtra()语句就可以了,无需关注内部具体的执行逻辑。 我检查了L~P的InboundSmsHandler.java代码,此方法应该完全通用。

通过反射取得 phoneId mPhone.getPhoneId()

public int getPhoneIdByReflection(XC_MethodHook.MethodHookParam param)  {
    XposedBridge.log("class name:" + param.thisObject.getClass().getName());
    try {
        Field field = null ;
        Class<?> clazz = param.thisObject.getClass() ;
        for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField("mPhone") ;//Tip: mPhone 可能在父类中
                break;
            } catch (Exception e) {
                //do nothing 
            }
        }
        field.setAccessible(true);
        Object mPhone = field.get(param.thisObject);

        Method method = Class.forName("com.android.internal.telephony.Phone").getDeclaredMethod("getPhoneId");
        int phone_id = (Integer) method.invoke(mPhone);
        return phone_id ;
    }catch (Exception e){
        Xlog.e(e.getMessage());
    }
    return  -1;
}

通过反射执行方法 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());

    int phoneId = getPhoneIdByReflection(param);
    //SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
    try{
        //Class<SubscriptionManager> c = SubscriptionManager.class;
        Class<?> c = Class.forName("android.telephony.SubscriptionManager");
        Method method = c.getMethod("putPhoneIdAndSubIdExtra",Intent.class,int.class);
        method.setAccessible(true);
        method.invoke(null, intent,phoneId);
    }catch (Exception e){
        XposedBridge.log(e);
    }

以上代码,在我设备(7.1.1)上进行测试,它正常工作,subid也是期望值。 另外我检查了以下Android代码,方法应该是通用的。希望能帮上忙。 http://androidxref.com/9.0.0_r3/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/8.1.0_r33/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/8.0.0_r4/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/7.1.2_r36/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/7.1.1_r6/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/7.0.0_r1/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/6.0.1_r10/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/6.0.0_r5/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/6.0.0_r1/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/5.1.1_r6/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/5.1.0_r1/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java http://androidxref.com/5.0.0_r2/xref/frameworks/opt/telephony/src/java/com/android/internal/telephony/InboundSmsHandler.java

apsun commented 4 years ago

A lot of that code can be simplified by using the XposedHelper APIs which takes care of the reflection :-)

Object phone = XposedHelpers.getObjectField(param.thisObject, "mPhone");
int phoneId = XposedHelpers.callMethod(phone, "getPhoneId");
XposedHelpers.callStaticMethod(SubscriptionManager.class, "putPhoneIdAndSubIdExtra", intent, phoneId);
Hovn commented 4 years ago

奈斯,对xposed api不那么熟,原来XposedHelper可以帮我们简化很多反射的代码,知识又增加了 ~ ps. 我重新测试了新代码段,它工作正常,subid的值正常。期待修复并关闭它。 :-)