微信插件之護妻寶(微信自動回覆機器人)1

前介

更多精彩文章,點這裏

現如今最常用的社交工具是什麼呢?必然是 微信 ,不得不說現在的微信不在簡簡單單是一個社交程序,它已經和我們生活緊密相關了。

做爲一個程序員?最重要的是什麼呢?

我認爲做爲一個程序員最重要的是

但是做爲一個程序員沒日沒夜的敲代碼,經常沒有時間回覆女朋友的微信或者尬聊

做爲一個資深的程序員,怎麼能被這種小挫折打敗呢?我們要用程序的去維護我們的 嬌妻因此我要開發一款自動回覆女朋友消息的微信插件。

你的 嬌妻 再也不能打斷你 打遊戲codeing風流快活 ,讓你的 嬌妻 和機器人聊吧(維護家庭和諧)!

確定目標

  1. 自動回覆高情商話術
  2. 微信無感知回覆,在後臺也不能遺漏回覆
  3. 開關設置,能指定自動回覆的嬌妻(我們的嬌妻可不止一個)
  4. 熱修復,兼容多版本微信
  5. 找到女朋友

方案定製

我個人習慣,再開發之前,先做準備。把思路屢清楚事半功倍。

  1. 自動回覆高情商話術

我的第一想法是先去下載類似 戀愛話術App ,然後逆向其接口,接入我們的程序。

本人下載了將近 10 款這種類型的應用,最終都發現收費很貴,並且話術都不是很全,最後放棄了。

然後找到了 圖靈機器人 ,提供完整的聊天機器人 API,並且話術還是很精湛的,因此果斷註冊開發中賬號。

  1. 微信無感知回覆,後臺也不能遺漏回覆

這種沒有很好的辦法,因爲要想在後臺回覆,只能想辦法向微信注入我們的代碼(也就是說我們的代碼要在微信的進程中運行,在專業點就是我們要想辦法拿到微信的 ClassLoader)。

我的方案還是使用 Xposed 框架 + 分身大師 實現免 Root 注入。

如何開發 Xposed 插件,請參看本人文章 參考鏈接

  1. 開關設置,能指定自動回覆的嬌妻(我們的嬌妻可不止一個)

逆向分析微信的聊天界面,找一個合適的地方,通過 Xposed 注入一個開關按鈕。這個難點是分析微信代碼,並且尋找到的 Hook 代碼儘量保證版本兼容(我不希望微信版本更新,就要重新發布或安裝插件)。

這裏補充下,爲啥要 Hook 點儘量保證版本兼容呢?其實說白了就是尋找微信沒有混淆的點做入口。爲啥呢?因爲一旦微信版本升級,肯定會再次混淆。若你 Hook 點是混淆的,那若微信版本升級,若要兼容新版本必定要從新尋找混淆後新的 Hook 點(我們可不得不到微信混淆後的 mapping 文件)。

  1. 熱修復,兼容多版本微信

在第 3 點我講過,儘量尋找沒有混淆的點做 Hook,但是若死活尋找不到沒有混淆的 Hook 點呢?我們只能想辦法進行動態修復插件了,讓用戶無感知使用,我提供的方案是通過 DexClassLoader 去修復 Hook 點代碼。
這套技術方案我在曾經在我的項目 微信語音助手 使用過(以停止維護),具體方案看下圖。

  1. 找到女朋友

若有女程序員看到這裏

開始敲代碼

都準備好啦,接下來我們開始

對接圖靈機器人

這個沒啥好說的就是去註冊賬號,查看提供的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 點,我找了很久,也想盡了所有的辦法,包括構造上面的開關按鈕 HookChatFooter 對象,通過調用 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哦)。

代碼倉庫

發佈了41 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章