如何用 Xposed 注册(代理)服务
最近写 Xposed 模块又遇到了一个问题
两个应用直接有没有什么好一点的方法进行交互?
我们知道,Xposed 模块和宿主应用一般不属于同一个进程,所以我们要解决的问题本质是
如何跨进程通信
目前一些解决方案
广播
目前比较主流的方法就是广播,但广播有以下缺点:
- 传输的对象必须是可序列化的
- 广播是单程的,如果需要完成一次交互则需要两次广播
- 无法确定对方的存活性
- 广播是开放的,有被拦截的风险
ContextProvider
也有使用 ContextProvider 去完成通信的,效果嘛,比广播好,但也有缺点,就是复杂
代理 Activity
Xposed 既然是模块,为啥要交互嘞,于是将模块Activity放入宿主App,也不失一种方法。
代理Service
既然Activity能代理,那Service能代理吗?那我们先理一下Service的流程
流程
服务端
- 写
aidl
接口和Stub
实现 - 写一个基于
Service
子类,实现fun onBind(intent: Intent?): IBinder
方法,方法体里面返回aidl
的实现 - 然后去
AndroidManifest.xml
注册Service
,添加Intent
过滤器
客户端
- 利用
Context#bindService()
进行绑定服务- Intent:符合过滤器
- ServiceConnection:连接器
- onServiceConnected(ComponentName name, IBinder service):连接回调
- ComponentName:目标组件
- IBinder:代理服务,通过
aidl.Stub#asInstance()
即可获得服务
- void onServiceDisconnected(ComponentName name):断开回调
- onServiceConnected(ComponentName name, IBinder service):连接回调
细节
好,代码都会写了。我们就知道两个应用中传递了一个IBinder
,通过IBinder
完成服务的代理。Xposed 完全可以实现在宿主里面创建一个Binder实例。那现在的问题是如何将这个Binder代理给到Xposed模块?
传输Binder
Binder本身并不是一个可序列化的对象,这就淘汰了很多跨进程的方式。
似乎没有什么好的办法了,
但是我们忽略了一个问题,在fun onBind(intent: Intent?): IBinder
方法里,并不会限制IBinder是哪个BinderasBinder()
出来的。
意思是,完全可以利用宿主中存在的Service
给Xposed模块的Service
开门,实现代理Service
实现
理论成立,说下简单的实现步骤
- 在宿主中找一个没啥用的
Service
,主要是模块容易连接的,比如export = true
或者IntentFilter
条件简单的 - 模块可以通过正常方式连接这个服务,但
Intent
中做好特定的标记 - Hook该
Service
的onBind
方法,通过判断Intent
中是否存在特定的标记从而选择返回模块中的Binder - 模块中可以直接使用
aidl#asInstance()
解析这个IBinder
从而完成Service
代理
后话
这种方法不仅适用于对普通应用进行Service
代理,也可以实现添加自定义系统服务
Ps:由于
Selinux
的存在,已经没法通过ServiceManager#addSerice()
添加自定义系统服务了,故而可以通过这个方法进行曲线救国。