前介
更多精彩文章,點這裏
現如今最常用的社交工具是什麼呢?必然是 微信
,不得不說現在的微信不在簡簡單單是一個社交程序,它已經和我們生活緊密相關了。
做爲一個程序員?最重要的是什麼呢?
我認爲做爲一個程序員最重要的是
但是做爲一個程序員沒日沒夜的敲代碼,經常沒有時間回覆女朋友的微信或者尬聊
。
做爲一個資深的程序員,怎麼能被這種小挫折打敗呢?我們要用程序的去維護我們的 嬌妻
因此我要開發一款自動回覆女朋友消息的微信插件。
你的 嬌妻
再也不能打斷你 打遊戲
丶 codeing
丶 風流快活
,讓你的 嬌妻
和機器人聊吧(維護家庭和諧)!
確定目標
- 自動回覆高情商話術
- 微信無感知回覆,在後臺也不能遺漏回覆
- 開關設置,能指定自動回覆的嬌妻(我們的嬌妻可不止一個)
- 熱修復,兼容多版本微信
- 找到女朋友
方案定製
我個人習慣,再開發之前,先做準備。把思路屢清楚事半功倍。
- 自動回覆高情商話術
我的第一想法是先去下載類似 戀愛話術
的 App
,然後逆向其接口,接入我們的程序。
本人下載了將近 10
款這種類型的應用,最終都發現收費很貴,並且話術都不是很全,最後放棄了。
然後找到了 圖靈機器人 ,提供完整的聊天機器人 API
,並且話術還是很精湛的,因此果斷註冊開發中賬號。
- 微信無感知回覆,後臺也不能遺漏回覆
這種沒有很好的辦法,因爲要想在後臺回覆,只能想辦法向微信注入我們的代碼(也就是說我們的代碼要在微信的進程中運行,在專業點就是我們要想辦法拿到微信的 ClassLoader
)。
我的方案還是使用 Xposed
框架 + 分身大師
實現免 Root
注入。
如何開發
Xposed
插件,請參看本人文章 參考鏈接
- 開關設置,能指定自動回覆的嬌妻(我們的嬌妻可不止一個)
逆向分析微信的聊天界面,找一個合適的地方,通過 Xposed
注入一個開關按鈕。這個難點是分析微信代碼,並且尋找到的 Hook
代碼儘量保證版本兼容(我不希望微信版本更新,就要重新發布或安裝插件)。
這裏補充下,爲啥要
Hook
點儘量保證版本兼容呢?其實說白了就是尋找微信沒有混淆的點做入口。爲啥呢?因爲一旦微信版本升級,肯定會再次混淆。若你Hook
點是混淆的,那若微信版本升級,若要兼容新版本必定要從新尋找混淆後新的Hook
點(我們可不得不到微信混淆後的 mapping 文件)。
- 熱修復,兼容多版本微信
在第 3
點我講過,儘量尋找沒有混淆的點做 Hook
,但是若死活尋找不到沒有混淆的 Hook
點呢?我們只能想辦法進行動態修復插件了,讓用戶無感知使用,我提供的方案是通過 DexClassLoader
去修復 Hook
點代碼。
這套技術方案我在曾經在我的項目 微信語音助手 使用過(以停止維護),具體方案看下圖。
- 找到女朋友
若有女程序員看到這裏
開始敲代碼
都準備好啦,接下來我們開始
對接圖靈機器人
這個沒啥好說的就是去註冊賬號,查看提供的API文檔
搭建 Xposed 項目
首先在項目 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
點,尋找儘量沒有混淆的 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點
前面說我們可能不光有一位嬌妻 我們需要一個開關,我可不想所有人都自動回覆,更不想和一個男的曖昧,更重要的是 圖靈機器人 收費啊。
最終通過不斷的斷點,分析,看源碼,找到了一個非常好的 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
,雖然微信不適用不代表,其他應用不適用,我曾經分析過 Facebook Messenge
聊天工具就用的這種方案。
最終我還是沒找到,沒混淆的發消息方法。
/**
* 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哦)。