# Xposed模块,主要功能是自动回复微信消息。
## 开发环境
+ Android Studio 3.5
+ 微信 7.0.6
## 准备工作
> 在 Android Studio 中新建项目这个直接略过。
1. 添加 **xposed** 依赖。
在 app 下的 build.gradle 中添加以下依赖
~~~
compileo
nly 'de.robv.android.xposed:api:82'
compileo
nly 'de.robv.android.xposed:api:82:sources'
~~~
2. 配置清单文件。
在 AndroidManifest.xml 中的 <application> 中添加以下内容
~~~
<me
ta-data
android:name="xposedmodule"
android:value="true" />
<me
ta-data
android:name="xposeddes
cription"
android:value="微信自动回复模块。" />
<me
ta-data
android:name="xposedminversion"
android:value="54" />
~~~
3. 编写主Hook类。
~~~
package com.wanzi.wechatrobot
im
port de.robv.android.xposed.IXposedHookLoadPackage
im
port de.robv.android.xposed.callbacks.XC_LoadPackage
class MainHook :IXposedHookLoadPackage{
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
}
}
~~~
4. 添加模块入口。
在 main 包下新建文件夹 assets ,并新建文件 xposed_init ,写入以下内容
~~~
com.wanzi.wechatrobot.MainHook
~~~
> 这里的内容是主Hook类的地址。
## 开始
> 这里主要分为两步,第一步是拦截微信数据库消息,第二步是调用微信方法发送消息。
### 拦截微信数据库
微信使用的数据库是他们自家的开源数据库 [WCDB](Tencent/wcdb),所以我们只需要去看一下他们的[api](https://tencent.github.io/wcdb/references/android/reference/com/tencent/wcdb/databa
se/SQLiteDataba
se.html),找出 **插入数据** 的方法,然后通过 hook 这个方法,就可以获取到我们需要的数据。
通过查看 api 和了解一些 SQL 常识,我们可以大概判断插入数据是这个方法[insert](https://tencent.github.io/wcdb/references/android/reference/com/tencent/wcdb/databa
se/SQLiteDataba
se.html#insert(java.lang.String,%20java.lang.String,%20android.content.ContentValues)),下面我们就先 hook 下这个方法看看。
~~~
class MainHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
// 只关心微信包名
if (lpparam?.packageName != "com.tencent.mm") {
return
}
XposedHelpers.findAndHookMethod(
"com.tencent.wcdb.databa
se.SQLiteDataba
se",
lpparam.classLoader,
"insert",
String::class.java, // table
String::class.java, // nullColumnHack
ContentValues::class.java,
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
val table = param?.args?.get(0) as String
val values = param.args?.get(2) as Co
ntentValues
// WANZIzZ/WeChatRecord/blob/master/app/src/main/java/com/wanzi/wechatrecord/CoreService.kt
// 这个是我以前写过的一个破解微信数据库的代码,从这个里面我们可以知道 message 表就是聊天记录表,现在我们只关心这个表
if (table != "message") {
return
}
Log.i("Wanzi", "拦截微信数据库 values:${values}")
}
}
)
}
}
~~~
好了,写完以后,运行一下项目,然后在 Xposed 模块中启用我们的模块。
最后让其他人给我们的微信发一条消息,我们可以在 Android Sutdio 的 Logcat 中发现我们成功的拦截到了接收到的消息,
![日志1](http://ww1.sinaimg.cn/large/c43dae63gy1g6mctimgs8j21ct00ut8o.jpg)
我们需要的数据就在 values 中。
好了,拦截微信数据库已经成功,下面就是我们就要调用微信方法发送消息。
### 调用微信方法发送消息
要调用微信方法发送消息,首先就得要知道微信是调用哪些方法来发送的,这里我们用 Android SDK 自带的 Android Device Mo
nitor 来调试分析。
#### 开始调试
![monitor1](http://ww1.sinaimg.cn/large/c43dae63gy1g6md174gyqj21hc0t4win.jpg)
![monitor2](http://ww1.sinaimg.cn/large/c43dae63gy1g6md2xqx9tj21hc0t4tdg.jpg)
点 **OK** ,然后在微信聊天页面随便发送一条消息。
![monitor3](http://ww1.sinaimg.cn/large/c43dae63gy1g6md5jxiscj21hc0t4q77.jpg)
发送消息以后,再点圈中的按钮。
![monitor4](http://ww1.sinaimg.cn/large/c43dae63gy1g6mdaw7srvj21hc0t47b8.jpg)
这样就会生成分析文件。
既然是点击事件,肯定和 click 有关,所以我们可以先搜索 click
![monitor5](http://ww1.sinaimg.cn/large/c43dae63gy1g6me1g05moj212t0os77p.jpg)
然后一步步往下找
![monitor6](http://ww1.sinaimg.cn/large/c43dae63gy1g6me2zyat0j20j0047wei.jpg)
果然没错,走到了微信的点击事件,接着再往下走
![monitor7](http://ww1.sinaimg.cn/large/c43dae63gy1g6me4lpd3uj20kz061t8z.jpg)
我们发现,一共调用了4个方法,这4个方法中,只有 `TQ` 最让我们起疑,为什么呢?大家看一下, `TQ` 的参数是字符串,这个字符串会不会就是消息内容呢?返回的是 Boolean ,会不会就是消息是否发送成功呢?我们来 Hook 一下试试。
~~~
XposedHelpers.findAndHookMethod(
"com.tencent.mm.ui.chatting.p",
lpparam.classLoader,
"TQ",
String::class.java,
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
val str = param?.args?.get(0) as String
Log.i("Wanzi", "拦截TQ str:$str 结果:${param.result}")
}
}
)
~~~
运行一下代码,然后我们发送一条微信消息,看下 Logcat 日志:
![日志2](http://ww1.sinaimg.cn/large/c43dae63gy1g6mehbqhdcj20de00pa9v.jpg)
哈哈,果然就是它了,我们再往下找。
![monitor8](http://ww1.sinaimg.cn/large/c43dae63gy1g6mes9vo7rj20ja0480st.jpg)
可以看到,调用了2个方法,这两个方法里面,只有 `avz ` 看起来最可疑,我们继续往下追踪
![monitor9](http://ww1.sinaimg.cn/large/c43dae63gy1g6meufkzucj20fe03oq2x.jpg)
这里看到,只调用了1个方法,参数是一个字符串,一个整数,返回值还是一个 Boolean,这个字符串应该还是消息内容,我们想一想,消息内容有了,是不是还缺少一个消息接收者?下面我们通过分析下微信源码来找找消息接受者在哪里。
#### 分析微信源码
通过反编译微信,我们可以得到微信源码。
> 我这里使用的是 [jadx](skylot/jadx) 来反编译的。
分析 `chatting.c.ai.eS`
~~~
private boolean eS(String str, final int i) {
int i2 = 0;
AppMethodBeat.i(31684);
final String arL = bo.arL(str);
if (arL == null || arL.length() == 0) {
ab.e("MicroMsg.ChattingUI.SendTextComponent", "doSendMessage null");
AppMethodBeat.o(31684);
return false;
}
this.Aro.avn(arL);
bz bzVar = new bz();
bzVar.cIT.cIV = arL;
bzVar.cIT.co
ntext = this.ctY.AsT.getCo
ntext();
bzVar.cIT.username = this.ctY.getTalkerUserName();
com.tencent.mm.sdk.b.a.yVI.l(bzVar);
if (bzVar.cIU.cIW) {
AppMethodBeat.o(31684);
return true;
}
boolean z = WXHardCoderJNI.hcSendMsgEnable;
int i3 = WXHardCoderJNI.hcSendMsgDelay;
int i4 = WXHardCoderJNI.hcSendMsgCPU;
int i5 = WXHardCoderJNI.hcSendMsgIO;
if (WXHardCoderJNI.hcSendMsgThr) {
i2 = g.We().dAB();
}
this.Arp = WXHardCoderJNI.startPerformance(z, i3, i4, i5, i2, WXHardCoderJNI.hcSendMsgTimeout, 202, WXHardCoderJNI.hcSendMsgAction, "MicroMsg.ChattingUI.SendTextComponent");