Xposed插件实践,去掉BiliBili的广告

环境准备

  • 一个刷好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一些烦人的东西" />
<!-- 模块最低支持的Api版本 一般填54即可 -->
<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)
//TODO
}
})

}
}

我们的逻辑就在XC_MethodHook里面写的, 现在可以什么都不写,直接安装到手机里面,在lsposed模块里面能看到我们写的模块

3.一些调试踩坑

安装的时候,关掉AS的部署优化,不生效的话,可以多加一些log或重启一次手机来验证

反解bilibili apk

通常的思路是我们得找到我们需要去掉的功能关键代码,例如我需要关掉这个广告,我这里的思路是

先找id,通过id定位代码,在代码里面定位具体的view,然后通过反解apk查找对应的逻辑,通过hook方法修改对应的逻辑

1.找view的id

这里推荐一个开发助手,可以简单抓一下布局的id,这个简单的原理可以通过 获得一些信息

1
adb shell dumpsys activity top


例如这里我们就找到了这个R.id.ad_tint_frame的id,接下来我们就是jadx里面找个id,有时候id不是一样的,可以搜索ad_tint_frame

这里我们重要的是拿到这个id值,在app里面通过这个id值,查到对应的view,就可以操作了

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()
//这里可以暴力的gone掉view
val rootLayout = curActivity?.findViewById<View>(R.id.content)
rootLayout?.post {
rootLayout.postDelayed({
//R.id.ad_tint_frame
hookRemoveView(rootLayout, 2131296637, context)
}, 500)
}
}
})

在hook里面,我们可以delay 久一点,保证这个view 已经被加载了,这里主要是做测试,具体的时间我们可以再仔细调节,因为view的加载时机还是很多的。

3.优化hook逻辑

在hook的代码里面,我们获取到对应具体实例类,然后去看对应的类里面能不能做通用的hook逻辑,因为如果是简单hook gone的话,不是很通用,我们也不能hook 其他一些创建的逻辑,给null值话,会触发很多nep,我们也很难取做兼容逻辑。
在代码里面打印这个view实例

然后去看这个实例的一些逻辑
比如有个构造的逻辑,那么我在这个构造的逻辑里面获取这个view的引用,就可以了

在构造的时候,获取引用,置为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