内容简介
微信自动回复插件,基于 Xposed 开发,可以给女朋友一个聊天机器人,她再也不会打扰我们敲代码了!
前介
微信
,不得不说现在的微信不在简简单单是一个社交程序,它已经和我们生活紧密相关了。做为一个程序员?最重要的是什么呢?
我认为做为一个程序员最重要的是
但是做为一个程序员进程没日没夜的敲代码,经常没有时间回复女朋友的微信或者 尬聊
。
我做为一个资深的程序员,怎么能被这种小挫折打败呢?我们要用程序的去维护我们的 娇妻
因此我要开发一款自动回复女朋友消息的微信插件。
你的 娇妻
再也不能打断你 打游戏
丶 敲代码
丶 风流快活
,让你的 娇妻
和机器人聊吧(维护家庭和谐)!
确定目标
自动回复高情商话术
微信无感知回复,在后台也不能遗漏回复
开关设置,能指定自动回复的娇妻
热修复,兼容多版本微信
找到女朋友
方案定制
我个人习惯,再开发之前,先做准备。把思路屡清楚事半功倍。
自动回复高情商话术
我的第一想法是先去下载类似 恋爱话术
的 App
,然后逆向其接口,接入我们的程序。
本人下载了将近 10
款这种类型的应用,最终都发现收费很贵,并且话术都不是很全,最后放弃了。
然后找到了 图灵机器人 ,提供完整的聊天机器人 API
,并且话术还是很精湛的,因此果断注册开发者账号。
微信无感知回复,在后台也不能遗漏回复
这种没有很好的办法,因为要想在后台回复,只能想办法向微信注入我们的代码(也就是说我们的代码要在微信的进程中运行,在专业点就是我们要想办法拿到微信的 ClassLoader
)。
我的方案还是使用 Xposed
框架 + 分身大师
实现免 Root
注入。
如何开发
Xposed
插件,请参看本人文章 查看链接
开关设置,能指定自动回复的娇妻(我们的娇妻可不止一个)
逆向分析微信的聊天界面,找一个合适的地方,通过 Xposed
注入一个开关按钮。这个难点是分析微信代码,并且寻找到的 Hook
代码尽量保证版本兼容(我不希望微信版本更新,就要重新发布或安装插件)。
这里补充下,为啥要
Hook
点尽量保证版本兼容呢?其实说白了就是寻找微信没有混淆的点做入口。为啥呢?因为一旦微信版本升级,肯定会再次混淆。若你Hook
点是混淆的,那若微信版本升级,若要兼容新版本必定要从新寻找混淆后新的Hook
点(我们可不得不到微信混淆后的 mapping 文件)。
热修复,兼容多版本微信
在第 3
点我讲过,尽量寻找没有混淆的点做 Hook
,但是若死活寻找不到没有混淆的 Hook
点呢?我们只能想办法进行动态修复插件了,让用户无感知使用,我提供的方案是通过 DexClassLoader
去修复 Hook
点代码。
这套技术方案我在曾经在我的项目 微信语音助手 使用过(以停止维护),具体方案看下图。
找到女朋友
开始敲代码
这个没啥好说的就是去注册账号,查看提供的API文档
首先在项目 build.gradle
加入依赖。
dependencies {
...
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
...
}
注意一定要用
compileOnly
依赖。为啥呢?大家可以自行去看下Xposed
的原理。我这里就叙述这个问题了,不再本文范畴。
接下来创建上图中所提到的入口类。
class XpCore : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
MMCoreHook.hook(lpparam)
}
}
其实寻找 Hook
点就是一个细心的活,我们要在微信的代码海中去寻找合适的 Hook
点,寻找尽量没有混淆的 Hook
点。
object MsgHook {
fun hook(actvity: Activity) {
hookFun(
{
if (TextUtils.equals("message", it.args[0].toString())) {
try {
val contentValues = it.args[2] as ContentValues
contentValues.apply {
val msgSvrId = getAsString("msgSvrId")
if (TextUtils.isEmpty(msgSvrId)) {
return@hookFun
}
val talker = getAsString("talker")
val type = getAsString("type")
val content = contentValues.getAsString("content")
Core.msgGo(talker, type, content, actvity)
}
} catch (e: Exception) {
e.printStackTrace()
Core.toast("消息拦截失败!!")
}
}
},
{},
"com.tencent.wcdb.database.SQLiteDatabase",
actvity.classLoader,
"insert",
String::class.java,
String::class.java,
ContentValues::class.java
)
}
}
前面说我们只想守护我们自己的 娇妻
所以需要一个开关,我可不想所有人都自动回复,更不想和一个男的暧昧,更重要的是 图灵机器人 收费啊。
最终通过不断的断点,分析,看源码,找到了一个非常好的 Hook
点。这个流程非常的长,我就不细讲了。具体如何逆向丶断点丶分析代码教程可以 参考 此文章。
部分核心代码,如下:
object ViewHook {
fun hook(actvity: Activity) {
log("view Hook开始")
hookFun({}, {
val viewGroup = it.thisObject as ViewGroup
val wxId = it.args[0] as String
Core.addClickView(viewGroup, wxId)
}, "com.tencent.mm.pluginsdk.ui.chat.ChatFooter", actvity.classLoader,
"setUserName",
String::class.java
)
}
}
来看看效果:
是否打开守护状态的数据,我使用的是
GreenDao
来进行存储的。我挺喜欢这个数据库框架的哦。
我们已经通过 Xposed
将我们的代码注入到微信当中了,并且获取到了微信的 ClassLoader
。有 ClassLoader
我们当然能通过反射去调用微信的方法啦。
这又是一个很长很长的过程,我就不叙述了,反正就是逆向丶断点丶分析。
在分析过程中我一直想寻找没有混淆的发消息 Hook
点,我找了很久,也想尽了所有的办法,包括构造上面的开关按钮 Hook
点 ChatFooter
对象,通过调用 View
的方法。
我这里就跑题下,我当时想寻找到一个合适的微信发消息的 Hook
点(没有混淆的点),顺便给大家分享下我当时巧夺天工的想法
我们前面分析到了 ChatFooter
对象,他是一个 FrameLayout
,提示这个布局就是我们聊天的布局,如下图:
思路很简单,我通过遍历 View
寻找到消息输入框,然后调用 setText
设置文本,然后在寻找 发送
按钮,在调用其 performClick
方法。
然后查看微信 ChatFooter
源码,在其构造方法中看到了。
接下来寻找发送消息按钮,我是通过搜索 setOnClickListener
方法,搜索到的。
接下来只需要递归遍历,寻找到消息输入框的 View
对象。
/**
* 递归遍历寻找 微信输入框 EditText 对象
*/
private fun findMsgEditText(view: View): EditText? {
if (view is EditText) {
return view
} else {
if (view is ViewGroup) {
for (index in 0 until view.childCount) {
val editText = findMsgEditText(view.getChildAt(index))
if (editText != null) {
return editText
}
}
}
}
return null
}
在开发过程中我发现个优化点,现在我们是通过递归查找到了,输入框
EditText
对象,本来还需要通过递归查找发送按钮的View
对象,这样效率有点低,但是突然我想到了输入框,按回车也能发消息,因此从新去看了下微信代码,如下图:
啊哈哈,更能确定我的 View
对象是对的,然后就是通过反射获取到了 EditText
对象的 OnEditorActionListener
对象,然后调用了 onEditorAction
方法(由于无效,代码删除了,找不到了),这样如果能成功,我们的 护妻宝
就能兼容所有微信版本啦,也不用做什么热修复啦,好开心测试下。
既然无效,不应该啊。接下里我断点看了下,发送按钮的点击事件方法。
一直到我看到 ChatFooter
的这个方法。
死的心都有了,这个 xTi
对象又被混淆了,还是不行啊!回头我又想了想,腾讯微信这种大厂怎么可能将 View
层和逻辑层代码写在一起呢?
最终这种方案我还是放弃了,花费了这么长的篇幅讲这个,只是给大家提供一个思路,有时候我们可以通过这种手段去 Hook
,虽然微信不适用不代表,其他应用不适用,我曾经分析过 FacebookMessenge
聊天工具就用的这种方案。
最终我还是没找到,没混淆的发消息方法。
/**
* hook 发消息方法
*/
private fun receiverMsg(activity: Activity, msg: String, talker: String) {
val azClzz = XposedHelpers.findClass("com.tencent.mm.model.az", activity.classLoader)
val obj = XposedHelpers.callStaticMethod(azClzz, "ZS")
val msgClzz = XposedHelpers.findClass("com.tencent.mm.modelmulti.h", activity.classLoader)
XposedHelpers.callMethod(obj, "b", XposedHelpers.newInstance(msgClzz, talker, msg, 1))
}
若有有能人,能找到没有混淆的发消息的
Hook
(尽量兼容所有版本的),并且愿意无私提供给我,请留言哦。
其实我们大部分工作完成了,但是热修复还需要很多注意点。由于篇幅问题(代码还没写,哈哈)。
但是我们方案有了,其实大家也应该看我代码注意到,我在构建代码的时候,已经将核心的注入方法都抽取在了 Core
类,为了就是将来热修复方便。
总结
首先由于 图灵机器人 开发者账号,没审核下来(审核2天了)。我暂时没有集成,只是到收到 娇妻
信息后,自动回复 你好可爱哦
。
当前效果
补充
由于作者分析的微信版本是 7.0.7
,等软件完成后在兼容最新版本微信,若想测试,可以安装微信 7.0.7
版本测试哦。
我是用我公司的 360分身大师X版 做的免 Root
使用 Xposed
(微信版本是7.0.7哦)。
代码仓库:https://github.com/makeloveandroid/ProtectLove
参考资料:
图灵机器人
http://www.turingapi.com/
Xposed插件开发
https://wenyingzhi.com/mu-lu-3/android-ni-xiang-fen-xiang
微信语音助手
https://wenyingzhi.com/mu-lu-3/wei-xin-yu-yin-zhu-shou
图灵机器人Api文档
http://docs.turingos.cn/
Android逆向技巧
https://wenyingzhi.com/mu-lu-3/android-ni-xiang-shi-zhan-zhi-mo-mo-miao-qiang-hong-bao
360分身大师X版
https://a.app.qq.com/o/simple.jsp?pkgname=com.qihoo.magic.xposed
--END--
识别二维码,关注我们