-
Notifications
You must be signed in to change notification settings - Fork 444
快速上手
WechatSpellbook推荐使用Android Studio 3.0.1及以上版本进行开发,对开发端的操作系统没有强制要求。另外教程中出现的代码均由Kotlin编写,但是遇到Java中没有的概念会停下来额外讲解一下。
测试手机推荐使用以下配置:
- Android 5.0 ~ 6.0.1
- Xposed Framework v89及以上 (卡刷版或Magisk版均可)
该配置目前最为稳定,不会因为框架级甚至系统级的Bug干扰正常开发。
这里使用默认配置新建项目即可,需要强调的是,虽然WechatSpellbook目前最低支持Android 4.4,但是已经不再耗费精力对4.4进行兼容性测试了。想要保证稳定性的话,最好还是从Android 5.0开始支持。
项目创建完成后,应该会有一个默认的模块叫做“app”,接下来的开发将围绕它展开。
打开app/build.gradle脚本,在dependencies结尾部分添加如下依赖:
dependencies {
// ......
//noinspection GradleDependency
compileOnly 'de.robv.android.xposed:api:53'
compileOnly 'de.robv.android.xposed:api:53:sources'
}
目前WechatSpellbook支持两种接入方式:使用Maven库或直接拉取GitHub项目。使用Maven库极其简单,但更新较慢。而直接拉取GitHub项目能获取开发中的版本。
打开app/build.gradle脚本,在dependencies结尾部分添加如下依赖:
dependencies {
// ......
// 添加编译好的Spellbook库
implementation 'com.github.gh0u1l5:wechat-spellbook:0.0.5'
// 添加Spellbook源码方便查询注释
implementation 'com.github.gh0u1l5:wechat-spellbook:0.0.5:sources'
}
注意:文档中的版本号未必是最新的,目前WechatSpellbook的最新版本为
如果刚刚新建的项目并非是Git项目,仅仅是一个本地文件夹的话,可以直接用clone命令将WechatSpellbook克隆到spellbook文件夹中。
git clone https://github.com/Gh0u1L5/WechatSpellbook.git spellbook
如果新建的项目是一个Git项目,那么可以使用submodule命令将WechatSpellbook拉取到spellbook文件夹中。
git submodule add https://github.com/Gh0u1L5/WechatSpellbook.git spellbook
然后将spellbook文件夹作为一个Module导入到当前项目中,并在app/build.gradle中添加如下依赖
dependencies {
// ......
implementation project(':spellbook')
}
首先,在AndroidManifest.xml文件中,添加以下内容到<application>块,让Xposed框架能够在安装的时候认出这个应用是Xposed插件
<application
<!-- ...... -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="@string/app_name" />
<meta-data
android:name="xposedminversion"
android:value="53" />
</application>
一个Xposed插件可以提供多个类作为入口。假设应用包名为com.gh0u1l5.wechatexample,入口类名叫WechatHook。那么就需要在app/src/main/assets文件夹中,建立一个名为xposed_init的文本文件,填写
com.gh0u1l5.wechatexample.WechatHook
接下来,创建这个WechatHook类,并且实现IXposedHookLoadPackage接口
class WechatHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
try {
if (SpellBook.isImportantWechatProcess(lpparam)) {
XposedBridge.log("Hello Wechat!")
}
} catch (t: Throwable) {
XposedBridge.log(t)
}
}
}
这里有两点需要额外解释:
- 实现IXposedHookLoadPackage接口之后,在任意一个应用开启新进程时,Xposed都会唤醒插件、调用handleLoadPackage方法。所以需要使用isImportantWechatProcess来检查这个进程属不属于微信。
- 如果微信开机自启的话,一旦handleLoadPackage中出现任何一个未能catch的异常,都会导致系统反复重启花式重启。所以一定要保证在这个函数里用try完整地裹住所有代码。因为类似这样的地方还有不少,所以为了简化代码WechatSpellbook中特意提供了一个方法:
BasicUtil.tryVerbosely {
if (SpellBook.isImportantWechatProcess(lpparam)) {
XposedBridge.log("Hello Wechat!")
}
}
这段代码和之前的try-catch结构是一样的,只是长得更简洁好看而已。
接下来将应用编译、安装、在Xposed安装器中启用插件并重启。编译前记得在Android Studio的设置里关闭Instant Run,该功能与Xposed不兼容。
接下来打开微信,就能在Xposed的日志或者Logcat中看到类似这样的日志记录
03-23 10:31:59.474 5131-5131/? I/Xposed: Hello Wechat!
那么小试牛刀之后,让我们开始真正接触Spellbook框架。首先,创建一个类,或者说一个 Spellbook插件 。
object Alert : IActivityHook {
override fun onActivityStarting(activity: Activity) {
Toast.makeText(activity, "Hello Wechat!", Toast.LENGTH_LONG).show()
}
}
这个类实现了IActivityHook接口,它的功能极其简单,就是在一个Activity将要调用onStart的时候,弹出一个Toast。
“Spellbook插件”是对Spellbook框架中所有监视微信事件并进行处理的类的统称,通过实现Spellbook制定的接口并且在启动时进行注册,这个插件就能够从Spellbook接收到微信事件的回调。插件可以实现的接口有两种风格:EventCenter和HookerProvider,关于这两种风格的详情参见事件机制章节。
另外,给Java背景的开发者简单解释一下什么是“object”:所谓object,其实就是“有且只有一个实例的类”,也就是Kotlin下的单例模式。在声明了Alert这个object之后,想要访问它的话直接用Alert这个名字就可以了,比如调用onActivityStarting方法就是
Alert.onActivityStarting(activity)
由此可见,Java中常见的设计模式,在Kotlin里面也会存在,只是说把Java写十几行的问题用两三行解决罢了,想要从Java转Kotlin的朋友大可不必对这门新语言感到战战兢兢。
接下来,我们修改一下之前的入口函数,将刚刚写的Alert插件提供给Spellbook引擎
class WechatHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
tryVerbosely {
if (isImportantWechatProcess(lpparam)) {
SpellBook.startup(lpparam, listOf(Alert))
}
}
}
}
再次重复之前的安装步骤并重启,接下来当你随便打开一个微信窗口时,都会看到“Hello Wechat!”字样弹出来,满意?Are you okay?
好,在学会了走路之后,让我们稍微跑两步。接下来的例子中,我们来试着窃取用户收到的所有新消息,并且把它记录/上传。
object Message : IDatabaseHook {
override fun onDatabaseInserted(thisObject: Any, table: String, nullColumnHack: String?, initialValues: ContentValues?, conflictAlgorithm: Int, result: Long): Operation<Long> {
if (table == "message") {
log("New Message: $initialValues")
}
return super.onDatabaseInserted(thisObject, table, nullColumnHack, initialValues, conflictAlgorithm, result)
}
}
这个插件实现了IDatabaseHook,通过继承onDatabaseInserted函数,可以在数据库插入操作完成后截获插入的内容。写完重启之前,不要忘了把它添加到Spellbook的启动参数里。
class WechatHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
tryVerbosely {
if (isImportantWechatProcess(lpparam)) {
SpellBook.startup(lpparam, listOf(Alert, Message))
}
}
}
}
重启之后再看Xposed日志,就能发现自己和别人聊天收到的消息全都被悄悄记录下来了,是不是感到一阵恶寒?
那么快速上手就到这里了,在这篇文档里主要是初步演示了一下EventCenter风格的事件处理。在下一章节事件机制里,会再深入解释EventCenter和HookerProvider这两套风格的逻辑和应用场景。