有关 Xposed Hook 跨应用通讯 Content Provider 使用的踩坑记录

 

最近在尝试使用 Xposed Hook 其他软件.

Try 0x00

写代码时发现一个问题: Xposed Hook 方法后, 代码是以那个被 Hook 的软件身份运行的, 然而我的 MainActivity 保存下来的 preference 是以我这个插件的身份保存的, Hook时没有访问权限. 然而SharedPreference 的 MODE_WORLD_READABLE 在 Android 7 以上的版本已经被彻底移除, 使用会直接抛出异常.

Try 0x01

后来打算使用安卓推荐的标准 ContentProvider 的方法来传递数据, 可是在被 Hook 的软件中又不方便获取到一个 Context 用于获取 ContentResolver…

Try 0x02

之后又打算使用直接在代码中调用的方法. 举例:

负责GUI的主程序是 me.lyc8503.activity.MainActivity, 我想直接在这个 MainActivity 访问我的 Hook 相关方法 me.lyc8503.Hook.Hooktest, 可是直接出现了 java.lang.NoClassDefFoundError 异常.

同理: 从 Hook 的代码中直接访问 MainActivity 代码也是各种未初始化导致的 NullPointerException

Try 0x03

后来又发现了 Xposed 提供的 XSharedPreference 里面有一个 makeWorldReadable() 方法, 以为是一种对新版安卓安全机制的 workaround, 又试了好久…结果一翻文档:

public boolean makeWorldReadable ()

Tries to make the preferences file world-readable.

Warning: This is only meant to work around permission “fix” functions that are part of some recoveries. It doesn’t replace the need to open preferences with MODE_WORLD_READABLE in the module’s UI code. Otherwise, Android will set stricter permissions again during the next save.

This will only work if executed as root (e.g. initZygote()) and only if SELinux is disabled.

Returns
  • true in case the file could be made world-readable.

Try 0x04 Final

最后还是回到了 Android 文档里推荐的 Broadcast 和 Content Provider …

这里只是我的 UI 和被 Hook 代码之间的通讯, 用 Broadcast 好像不是很合适… 就最终用了 Content Provider.

最终的问题是我对 Context 的理解不够到位. 我误以为所有 Context 都是直接由 Activity.getContext() 获取的, 最后参考了这篇文章.

Context说明Context说明

可以看到Activity.Service.Application都是Context的子类;

也就是说,Android系统的角度来理解:Context是一个场景,代表与操作系统的交互的一种过程.从程序的角度上来理解:Context是个抽象类,而Activity.Service.Application等都是该类的一个实现.

再仔细看一下上图:Activity.Service.Application都是继承自ContextWrapper,而ContextWrapper内部会包含一个base context,由这个base context去实现了绝大多数的方法.

还有一张不同 Context 功能的图:

Context功能Context功能

大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task.一般情况不推荐.

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用.

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值.(可以无视)

注:ContentProvider.BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用.

好了,这里我们看下表格,重点看Activity和Application,可以看到,和UI相关的方法基本都不建议或者不可使用Application,并且,前三个操作基本不可能在Application中出现.实际上,只要把握住一点,凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏.

所以其实只需要使用 AndroidAppHelper.currentApplication().getApplicationContext() 就可以直接获得一个 Application Context, 进而用于获取 Content Resolver.

以此文记录因为对 Context 理解不透彻导致的踩坑.

本文采用 CC BY-NC-SA 4.0 许可协议发布.

作者: lyc8503, 文章链接: https://blog.lyc8503.net/post/xposed-cross-application-communication/
如果本文给你带来了帮助或让你觉得有趣, 可以考虑赞助我¬_¬