經(jīng)過了前段日子基于微信的學(xué)習(xí)饲宿,今天終于折騰除了一點干貨,微信聊天機(jī)器人迈勋。先上個圖
一對一聊天 | 群聊 |
---|---|
為了完成這個炬灭,人都蒙蔽了,不多說了開始分析把靡菇。
首先進(jìn)行如下圖的操作
DDMS | 軌跡 |
---|---|
左圖選中要記錄的進(jìn)程點擊左圖右上角那個圖標(biāo)進(jìn)行軌跡錄制之后重归,再次點擊這個停止錄制,就有了右邊的圖厦凤,我是從點擊微信發(fā)送按鈕開始錄制的鼻吮,所以在有圖下面輸入過濾關(guān)鍵字 onClick
public final boolean FZ(String str) {
mS(false);
ctQ();
return this.yOg.yRO.dt(str, 0);
}
從這個開始入手進(jìn)入 dt(String str, int i)
public final boolean dt(String str, int i) {
...此處省略...
this.ejx.cuJ().post(new y$1(this, Xf, i));
this.ejx.mZ(true);
return true;
}
接下來是關(guān)鍵點了,后面關(guān)聯(lián)的地方有多處泳唠,我一一列出來
首先是 y$1
package com.tencent.mm.ui.chatting.b;
import android.database.Cursor;
import com.tencent.mm.ac.l;
import com.tencent.mm.ai.a;
import com.tencent.mm.compatible.e.n;
import com.tencent.mm.modelmulti.i;
import com.tencent.mm.plugin.appbrand.jsapi.audio.d;
import com.tencent.mm.plugin.bbom.h;
import com.tencent.mm.plugin.report.service.g;
import com.tencent.mm.pluginsdk.ui.chat.ChatFooter;
import com.tencent.mm.sdk.platformtools.an;
import com.tencent.mm.sdk.platformtools.bh;
import com.tencent.mm.sdk.platformtools.w;
import com.tencent.mm.storage.aw;
import com.tencent.mm.storage.ax;
import com.tencent.mm.storage.bj;
import com.tencent.mm.storage.x;
import com.tencent.mm.z.au;
import com.tencent.mm.z.br;
import com.tencent.mm.z.q;
import com.tencent.mm.z.s;
import java.util.LinkedList;
class y$1 implements Runnable {
final /* synthetic */ String fNk;
final /* synthetic */ int ra;
final /* synthetic */ y yXJ;
y$1(y yVar, String str, int i) {
this.yXJ = yVar;
this.fNk = str;
this.ra = i;
}
public final void run() {
g.vX(20);
int i = (this.yXJ.ejx.cuz().field_username.equals("medianote") && (q.GG() & 16384) == 0) ? 1 : 0;
if (i != 0) {
this.yXJ.ejx.cuM();
au.Dv().a(new a(this.yXJ.ejx.cuz().field_username, this.fNk), 0);
return;
}
String cuB;
if (this.yXJ.ejx.cuP().getCount() == 0 && x.XM(this.yXJ.ejx.ctS())) {
br.ID().c(10076, new Object[]{Integer.valueOf(1)});
}
String ctS = this.yXJ.ejx.ctS();
int hC = s.hC(ctS);
String str = this.fNk;
q qVar = this.yXJ.yRE;
if (qVar.ejx.cuC()) {
w.i("MicroMsg.ChattingUI.LbsImp", "[oneliang]encrypt:" + qVar.ejx.wG() + ",raw:" + qVar.ejx.cuB());
cuB = bh.oB(qVar.ejx.wG()) ? qVar.ejx.cuB() : qVar.ejx.wG();
} else {
cuB = ctS;
}
if (bh.oB(cuB)) {
w.w("MicroMsg.ChattingUI.TextImp", "tempUser is null");
return;
}
ChatFooter cuS = this.yXJ.ejx.cuS();
int i2 = this.ra;
int i3 = cuS.vST.vTU.containsKey(ctS) ? ((LinkedList) cuS.vST.vTU.get(ctS)).size() > 0 ? d.CTRL_INDEX : i2 : i2;
l iVar = new i(cuB, str, hC, i3, this.yXJ.ejx.cuS().fu(ctS, str));
q qVar2 = this.yXJ.yRE;
if (qVar2.ejx.cuC()) {
aw awVar;
cuB = qVar2.klH;
ax SB = com.tencent.mm.bb.d.SB();
String wG = qVar2.ejx.wG();
Cursor b = SB.fOK.b("SELECT * FROM " + SB.getTableName() + " where sayhiencryptuser=? and isSend=0 and flag=0" + " ORDER BY createtime desc LIMIT 1", new String[]{wG}, 2);
if (b == null) {
awVar = null;
} else if (b.moveToFirst()) {
awVar = new aw();
awVar.c(b);
b.close();
} else {
b.close();
awVar = null;
}
if (!(awVar == null || bh.oB(awVar.field_ticket))) {
cuB = awVar.field_ticket;
}
if (bh.oB(cuB)) {
awVar = com.tencent.mm.bb.d.SB().YM(qVar2.ejx.wG());
if (!(awVar == null || bh.oB(awVar.field_ticket))) {
cuB = awVar.field_ticket;
}
}
if (cuB != null) {
iVar.gJj = new h(cuB);
}
}
au.Dv().a(iVar, 0);
if (s.hy(ctS)) {
au.Dv().a(new com.tencent.mm.plugin.setting.model.i(com.tencent.mm.compatible.e.q.zI(), this.fNk + " key " + bj.cnt() + " local key " + bj.cns() + "NetType:" + an.getNetTypeString(this.yXJ.ejx.cuH().getContext().getApplicationContext()) + " hasNeon: " + n.zj() + " isArmv6: " + n.zl() + " isArmv7: " + n.zk()), 0);
}
}
}
這個類就是 消息處理的過程 調(diào)用關(guān)鍵方法
au.Dv().a(l lVar, int i)
我在分析到這個地方被卡了很久
這個au.Dv().a(l lVar, int i) 有三種形式
以下面的方式調(diào)用可以實現(xiàn)自動回復(fù)狈网,自能自己看到宙搬,對方看不到消息
au.Dv().a(new a(this.yXJ.ejx.cuz().field_username, this.fNk), 0);
以下面的方式調(diào)用可以實現(xiàn)自動回復(fù)笨腥,自能自己看到,對方也能看到消息
l iVar = new i(cuB, str, hC, i3, this.yXJ.ejx.cuS().fu(ctS, str));
au.Dv().a(iVar, 0);
以下面的方式暫未試驗勇垛,有興趣的小伙伴可以自己嘗試調(diào)用脖母,如果方便的話可以以反饋給我
au.Dv().a(new com.tencent.mm.plugin.setting.model.i(com.tencent.mm.compatible.e.q.zI(), this.fNk + " key " + bj.cnt() + " local key " + bj.cns() + "NetType:" + an.getNetTypeString(this.yXJ.ejx.cuH().getContext().getApplicationContext()) + " hasNeon: " + n.zj() + " isArmv6: " + n.zl() + " isArmv7: " + n.zk()), 0);
接下來就是給上面的方法拼接參數(shù),等會給的代碼里面會提到闲孤。這里就不多重復(fù)了
到此我們能夠回復(fù)了谆级,那么我們回復(fù)的時機(jī)是什么呢烤礁?
于是我們需要找到什么時候收到消息,然后進(jìn)行調(diào)用上面的代碼
重復(fù)DDMS的操作進(jìn)行接收消息通知的篩選 如下圖
看到篩選后關(guān)鍵類com.tencent.mm.booter.notification.b$1
package com.tencent.mm.booter.notification;
import android.os.Looper;
import android.os.Message;
import com.tencent.mm.sdk.platformtools.ac;
import com.tencent.mm.sdk.platformtools.af;
import com.tencent.mm.sdk.platformtools.w;
class b$1 extends af {
final /* synthetic */ b fEw;
b$1(b bVar, Looper looper) {
this.fEw = bVar;
super(looper);
}
public final void handleMessage(Message message) {
super.handleMessage(message);
ac.getContext().getSharedPreferences("notify_prep", 0).edit().putBoolean("longNoopIntervalFlag", true).apply();
String string = message.getData().getString("notification.show.talker");
String string2 = message.getData().getString("notification.show.message.content");
int i = message.getData().getInt("notification.show.message.type");
int i2 = message.getData().getInt("notification.show.tipsflag");
w.i("MicroMsg.MMNotification", "notify need to deal: %s", new Object[]{string});
try {
if (message.what == 1) {
b.a(this.fEw, string, string2, i, i2, true);
} else {
b.a(this.fEw, string, string2, i, i2, false);
}
} catch (Throwable e) {
w.printErrStackTrace("MicroMsg.MMNotification", e, "showNotifiHandler", new Object[0]);
}
}
}
看著這關(guān)鍵字貌似挺匹配的肥照,于是進(jìn)行hook發(fā)現(xiàn)確實接收的時候會執(zhí)行這里
結(jié)合上面的得到如下代碼
@Override
public void autoRepeat() {
final Class<?> afClass = XposedHelpers.findClass("com.tencent.mm.sdk.platformtools.af", mClassLoader);
// //獲取收到消息的標(biāo)記通知欄
Class<?> b$1Class = XposedHelpers.findClass("com.tencent.mm.booter.notification.b$1", mClassLoader);
XposedHelpers.findAndHookMethod(b$1Class, "handleMessage", Message.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Message message = (Message) param.args[0];
final String string = message.getData().getString("notification.show.talker");
String string2 = message.getData().getString("notification.show.message.content");
int i = message.getData().getInt("notification.show.message.type");
int i2 = message.getData().getInt("notification.show.tipsflag");
LogUtils.i(string, string2, i, i2, afClass);
Class<?> gClass = XposedHelpers.findClass("com.tencent.mm.kernel.g", mClassLoader);
Object g = XposedHelpers.callStaticMethod(gClass, "Ea");
Object filedA = XposedHelpers.getObjectField(g, "fVR");
Class<?> oClass = XposedHelpers.findClass("com.tencent.mm.ac.o", mClassLoader);
Class<?> lClass = XposedHelpers.findClass("com.tencent.mm.ac.l", mClassLoader);
Method methodA = XposedHelpers.findMethodExact(oClass, "a", lClass, int.class);
Object o = XposedHelpers.callStaticMethod(oClass, "a", filedA);
//這里只能自己看到回復(fù)消息 對方看不到
// Class<?> aClass = XposedHelpers.findClass("com.tencent.mm.ai.a", mClassLoader);
// Object a = XposedHelpers.newInstance(aClass, new Class[]{String.class, String.class}, string, "haha");
// Object[] p = new Object[]{a, 0};
//調(diào)用這里可以實現(xiàn)自動回復(fù) 并發(fā)送到對方
Class<?> iClass = XposedHelpers.findClass("com.tencent.mm.modelmulti.i", mClassLoader);
Object io = XposedHelpers.newInstance(iClass, new Class[]{String.class, String.class, int.class, int.class, Object.class}, string, "haha", 1, 1, new HashMap<String, String>() {{
put(string, string);
}});
Object[] pp = new Object[]{io, 0};
// LogUtils.i(gClass, g, filedA, oClass, lClass, methodA, o, aClass, a, p, iClass, io, pp);
LogUtils.i(gClass, g, filedA, oClass, lClass, methodA, o, iClass, io, pp);
try {
// XposedBridge.invokeOriginalMethod(methodA, o, p);
XposedBridge.invokeOriginalMethod(methodA, o, pp);
} catch (Exception e) {
e.printStackTrace();
LogUtils.e(e.getLocalizedMessage());
}
LogUtils.i("send ok");
}
});
}
令我感到頭痛的是在進(jìn)行匹配參數(shù)脚仔,因為有些關(guān)聯(lián)的引用不是很明顯,找的時候有很多誤區(qū)舆绎。
根據(jù)以上內(nèi)容今后可以擴(kuò)展聊天機(jī)器人鲤脏,群助手,以及方便學(xué)習(xí)微信搶紅包的流程吕朵。哈哈