环境准备
- 一个刷好magisk面具的手机,然后安装个lsposed框架
- Android Studio + Jadx + bilibili.apk(测试版本7.69.0)
插件开发
1.默认配置
新建一个AS工程,添加依赖
1
| compileOnly 'de.robv.android.xposed:api:82'
|
在settings.gradle里面添加 xposed 依赖
1 2 3 4 5 6 7
| dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { maven { url 'https://api.xposed.info/' } } }
|
然后在AndroidManifest.xml里面添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <application <!-- 是否为Xposed模块 --> <meta-data android:name="xposedmodule" android:value="true"/> <meta-data android:name="xposeddescription" android:value="去除bilibili一些烦人的东西" /> <meta-data android:name="xposedminversion" android:value="54"/> <meta-data android:name="xposedscope" android:resource="@array/xposedscope"/> </application>
|
在arrays.xml里面添加作用域,就是作用包名
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="xposedscope" > <item>tv.danmaku.bili</item> </string-array> </resources>
|
新建一个xposed_init文件, app/src/main/assets/xposed_init,在里面指定一个MainHook入口文件,例如com.user.knock.bilib.hook.HookInit
2.Hook方法 findAndHookMethod
主要是XposedHelpers.findAndHookMethod来注册需要hook的方法,这里的方法和参数需要正确对应才会生效,需要的当前实例和方法参数都可以通过MethodHookParam来获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class HookInit : IXposedHookLoadPackage, IXposedHookZygoteInit { @Throws(Throwable::class) override fun handleLoadPackage(loadPackageParam: LoadPackageParam) { XposedHelpers.findAndHookMethod(Application::class.java, "attach", Context::class.java, object : XC_MethodHook() { @Throws(Throwable::class) override fun afterHookedMethod(param: MethodHookParam) } })
} }
|
我们的逻辑就在XC_MethodHook里面写的, 现在可以什么都不写,直接安装到手机里面,在lsposed模块里面能看到我们写的模块
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710064238475-4d3fd9d0-6b9a-491f-93a0-5fed055e07a8.png#averageHue=%23f6f5f8&clientId=u74a4cb6c-522d-4&from=paste&height=254&id=u1d2344e2&originHeight=634&originWidth=1434&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=197740&status=done&style=none&taskId=u19c2199c-0331-4f33-8034-6c64436149c&title=&width=573.6)
3.一些调试踩坑
安装的时候,关掉AS的部署优化,不生效的话,可以多加一些log或重启一次手机来验证
反解bilibili apk
通常的思路是我们得找到我们需要去掉的功能关键代码,例如我需要关掉这个广告,我这里的思路是
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710064690095-c6800ae6-6eec-4c42-a4d3-07d172ff370d.png#averageHue=%23cab7ad&clientId=u74a4cb6c-522d-4&from=paste&height=207&id=ucd2cde46&originHeight=518&originWidth=1502&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=257580&status=done&style=none&taskId=uc448188c-fd3e-4242-af0e-38ff170b3cd&title=&width=600.8)
先找id,通过id定位代码,在代码里面定位具体的view,然后通过反解apk查找对应的逻辑,通过hook方法修改对应的逻辑
1.找view的id
这里推荐一个开发助手,可以简单抓一下布局的id,这个简单的原理可以通过 获得一些信息
1
| adb shell dumpsys activity top
|
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710064942141-0d819572-ecd9-4868-a0bc-a8f3b922c39c.png#averageHue=%2363684b&clientId=u74a4cb6c-522d-4&from=paste&height=182&id=ud84018cb&originHeight=454&originWidth=1616&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=417734&status=done&style=none&taskId=u4935ef82-c6b8-49a0-a2dd-22aa4a6e2d9&title=&width=646.4)
例如这里我们就找到了这个R.id.ad_tint_frame的id,接下来我们就是jadx里面找个id,有时候id不是一样的,可以搜索ad_tint_frame
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710065043540-70a12efc-e166-4048-b6d6-c9bd9bfb31e4.png#averageHue=%23aaa7a0&clientId=u74a4cb6c-522d-4&from=paste&height=501&id=uf11993d7&originHeight=1252&originWidth=2244&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=512462&status=done&style=none&taskId=u14830d8f-e8e9-4a1f-854a-40999d12a99&title=&width=897.6)
这里我们重要的是拿到这个id值,在app里面通过这个id值,查到对应的view,就可以操作了
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710065065944-427d68b6-0c79-460e-8092-50156ea6a113.png#averageHue=%23fcfbe7&clientId=u74a4cb6c-522d-4&from=paste&height=78&id=ufccb3d77&originHeight=196&originWidth=2070&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=47341&status=done&style=none&taskId=u9b7ad9f1-447f-4521-95fa-723adafb973&title=&width=828)
2.查找view
查找view,我们一般先获取到当前的activity,然后find一个安卓的根布局,然后通过根布局查找我们的view。
打开对应的页面,获取当前页面的activity,这个方法很多,看activity或类似的topactivity工具即可。
去看对应的代码,hook onCreat方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| XposedHelpers.findAndHookMethod("com.bilibili.ship.theseus.detail.UnitedBizDetailsActivity", lpparam.classLoader, "onCreate", Bundle::class.java, object : XC_MethodHook() { override fun beforeHookedMethod(param: MethodHookParam?) { super.beforeHookedMethod(param) }
@SuppressLint("ResourceType") override fun afterHookedMethod(param: MethodHookParam?) { super.afterHookedMethod(param) val curActivity = param?.thisObject as? Activity Toast.makeText( context, "UnitedBizDetailsActivity show curActivity=$curActivity", Toast.LENGTH_SHORT ).show() val rootLayout = curActivity?.findViewById<View>(R.id.content) rootLayout?.post { rootLayout.postDelayed({ hookRemoveView(rootLayout, 2131296637, context) }, 500) } } })
|
在hook里面,我们可以delay 久一点,保证这个view 已经被加载了,这里主要是做测试,具体的时间我们可以再仔细调节,因为view的加载时机还是很多的。
3.优化hook逻辑
在hook的代码里面,我们获取到对应具体实例类,然后去看对应的类里面能不能做通用的hook逻辑,因为如果是简单hook gone的话,不是很通用,我们也不能hook 其他一些创建的逻辑,给null值话,会触发很多nep,我们也很难取做兼容逻辑。
在代码里面打印这个view实例
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710065645933-4f309353-1fb5-40af-aa26-3a8adb11db93.png#averageHue=%23c8d3c9&clientId=u74a4cb6c-522d-4&from=paste&height=98&id=uc06cec85&originHeight=244&originWidth=1498&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=310588&status=done&style=none&taskId=uad7fb3c1-9f61-49cc-8cfe-916beed950d&title=&width=599.2)
然后去看这个实例的一些逻辑
比如有个构造的逻辑,那么我在这个构造的逻辑里面获取这个view的引用,就可以了
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710065687579-52bf4fd6-8d61-4231-90d9-84b1e41b8222.png#averageHue=%23f6f6f6&clientId=u74a4cb6c-522d-4&from=paste&height=95&id=ua602aa41&originHeight=238&originWidth=2532&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=57845&status=done&style=none&taskId=u0d5d2ae5-036f-43fd-ac4a-1548d0de3fc&title=&width=1012.8)
在构造的时候,获取引用,置为gone之后,业务还有其他地方掉show的逻辑,我们可以添加一个addOnAttachStateChangeListener,在onViewAttachedToWindow的时候,再gone一次。保证view的不可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| XposedHelpers.findAndHookConstructor("com.bilibili.ad.adview.video detail.upper.AdUpperRootFrameLayout", lpparam.classLoader, Context::class.java, AttributeSet::class.java, Int::class.javaPrimitiveType, object : XC_MethodHook() { @Throws(Throwable::class) override fun beforeHookedMethod(param: MethodHookParam) { super.beforeHookedMethod(param) }
@Throws(Throwable::class) override fun afterHookedMethod(param: MethodHookParam) { super.afterHookedMethod(param) val frameLayout = param.thisObject as? ViewGroup frameLayout?.visibility = View.GONE frameLayout?.addOnAttachStateChangeListener(object :OnAttachStateChangeListener{ override fun onViewAttachedToWindow(p0: View) { p0.visibility = View.GONE (p0?.parent as? View)?.visibility = View.GONE } override fun onViewDetachedFromWindow(p0: View) {
} }) } })
|
最后多点几个,测试ok
![](https://cdn.nlark.com/yuque/0/2024/png/501385/1710065926900-2c0776e5-15ec-40d8-b655-f7d9b316877b.png#averageHue=%23dbe2c6&clientId=u74a4cb6c-522d-4&from=paste&height=95&id=u67def3c6&originHeight=238&originWidth=1490&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=235225&status=done&style=none&taskId=ud96fb84d-1af3-4501-aafa-bf60b1f5e08&title=&width=596)