想 hook 掉获取本机号码的方法,自己写的 demo 是成功的,但是设置里关于界面的本机号码还是没变,于是就想着去看看源码里设置里头的逻辑。在此之前,先说下事情起因
获取本机号码
正常来说,我们可以通过以下代码获取到本机号码
1 | try { |
这里需要动态申请 android.permission.READ_PHONE_STATE
权限,不在本文的讨论的范围,代码就不搬了
用模拟器得到本机号码为
1 | 本机号码:15555218135 |
设置界面
没错就是这个方法,hook 掉
1 | XposedBridge.hookAllMethods(TelephonyManager::class.java, "getLine1Number", object : XC_MethodHook() { |
利用 Xposed 来玩黑科技也不在本文讨论范围,以后会单独来讲 Xposed。现在就只要明白我这里把 getLine1Number()
的返回值修改成了 12345678900
就行了。运行看下效果
自己 Demo 的 Log
1 | 本机号码:12345678900 |
嗯,成功了,再看设置界面,What!~ 还是一样的,因为设置界面里头啥也没变,就不贴图了,看上面的就行了。
这下郁闷了,明明我把 getLine1Number()
方法的返回值修改了,而且自己的 Demo 也是成功的,为啥系统的设置界面没有改变呢?为了弄清楚,我需要知道设置界面里头获取本机号码的逻辑。可能调用的不是 getLine1Number()
方法,去看下源码吧
开启源码之旅
想看完整的源码,可以去官网下载对应的版本的源码,然后再利用专门的工具查看。不过这种方式有点得不偿失了,毕竟源码那么大。于是就想到了在线看源码的网站,这里重点推荐
界面长这样
选择对应的版本,因为模拟器用的是 6.0.0 ,所以我这里选择 Marshmallow - 6.0.0_r5
会看到如下界面,右侧用于选择源码中对应的架构
- Full Search: 进行全文搜索,会匹配所有的单词、字符串、标识符以及数字等,例如在 frameworks 下通过 Full Search 搜索”Settings“
- Definition:搜索符号定义相关的代码,例如搜索 ondraw 函数的定义,显示结果如图
- Symbol:搜索符号,例如可以搜索类中的成员变量等,下图显示了通过 Symbol 搜索FEATURE_NO_TITLE的结果
- File Path:搜索源码文件名中包含给定字符串的文件,例如想要搜索文件名包含 mediacodec的源码文件,则可以在 File Path 中填入 mediacodec 进行搜索,结果如下所示
利用搜索确实可以快速定位到自己的需要的代码位置,但也可能不是想要的,所以别纠结为啥搜出来的不是自己想要的
首先我需要知道设置界面所属的 Activity,利用 adb 指令
1 | adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p' |
adb 指令查看当前与用户交互的 Activity 可以在以下文章查看
https://blog.csdn.net/hty1053240123/article/details/54312614
显示结果
1 | $ adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p' |
如果不需要看到所有栈里的 Activity,只想知道当前 Activity 的界面,可以用如下指令
1 | adb shell dumpsys activity | grep "mFocusedActivity" |
结果
1 | $ adb shell dumpsys activity | grep "mFocusedActivity" |
这样我们就知道了,显示本机号码的这个界面的 Activity 叫 SimStatus
,其中com.android.settings
是包名,设置
在手机里也是一个应用,包名就是com.android.settings
知道了要查看的类,现在我们去搜索下
其实如果我们对 Android 系统有些了解的话,就应该知道所有系统里面的应用,都在 packages 工程下面,所以在选择工程的时候,选择对应的工程,会给我们过滤掉很多无关的结果,如
这个不是很明显,只少了3个结果,不过我们已经找到想找的类了,点击 SimStatus
进去,简单查看之后,找到了关键代码
这就是 6.0.0 系统里头,关于本机号码的代码,一看就明白为啥我 hook 掉 getLine1Number()
,但是设置界面里头却没有改变,本来想直接getLine1NumberForSubscriber()
方法 hook 掉就可以了,但自己在 Android Studio 去查看 TelephonyManager
源码的时候,发现没有 getLine1NumberForSubscriber()
这个方法,因为我用的 SDK 源码是 android-27,也就是 8.1 的系统。既然不同系统里头 TelephonyManager
的源码不同,那设置界面的源码可能也会不同。为了验证下,打开一个 8.0 系统的模拟器(因为有一个现成的)
利用 adb 指令查看到当前界面的 Activity
1 | $ adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p' |
这里又发现一个问题,那就是
1 | adb shell dumpsys activity | grep "mFocusedActivity" |
这个指令得不到想要的结果,没办法用查看到所有的了
通过上面的结果,我们知道我们想要的界面的 Activity 是 SimStatusActivity
,那去搜索一下试试
静态内部类,实现里头还是空的,这里不去分析怎么找到具体代码了,我们返回一步,来到 AndroidManifest.xml
,找到 SimStatusActivity
点击里头的 com.android.settings.deviceinfo.SimStatus
然后再来到 DeviceInfoUtils
发现这里又是调用的 getLine1Number()
方法了,果然不同系统不同。不同也发现一个相同的地方,那就是他们最后都会调用
1 | PhoneNumberUtils.formatNumber(rawNumber) |
更改策略,把 formatNumber()
方法 hook 掉
1 | XposedBridge.hookAllMethods(PhoneNumberUtils::class.java, "formatNumber", object : XC_MethodHook() { |
现在再来看设置界面的本机号码
OK,成功了!
因为想玩点黑科技,又去拾起了系统源码,虽然很浅显,但也明白源码并不惧怕,以后如果需要找点什么,也不会一头雾水了,关于文中提到的 Xposed ,将会在另一篇文章中简单介绍。
仅以此记录。