首页
论坛
专栏
课程

[原创]Xposed第五课(微信篇) 聊天机器人____群聊小助手n(*≧▽≦*)n

KingZd 2018-6-12 23:46 5672

上一篇[原创]Xposed第四课(微信篇) 朋友圈点赞(2)之好友列表

 

经过了前段日子基于微信的学习,今天终于折腾除了一点干货,微信聊天机器人。先上个图

一对一聊天 群聊
device-2018-06-12-220825.gif device-2018-06-12-221655.png
 

为了完成这个,人都蒙蔽了,不多说了开始分析把。

 

首先进行如下图的操作

DDMS 轨迹
QQ截图20180612223149.png QQ截图20180612223157.png
 

左图选中要记录的进程点击左图右上角那个图标进行轨迹录制之后,再次点击这个停止录制,就有了右边的图,我是从点击微信发送按钮开始录制的,所以在有图下面输入过滤关键字 onClick

public final boolean FZ(String str) {
        mS(false);
        ctQ();
        return this.yOg.yRO.dt(str, 0);
    }

从这个开始入手进入 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;
    }

接下来是关键点了,后面关联的地方有多处,我一一列出来
首先是 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);
        }
    }
}

这个类就是 消息处理的过程 调用关键方法
au.Dv().a(l lVar, int i)

 

我在分析到这个地方被卡了很久

 

这个au.Dv().a(l lVar, int i) 有三种形式

以下面的方式调用可以实现自动回复,自能自己看到,对方看不到消息

au.Dv().a(new a(this.yXJ.ejx.cuz().field_username, this.fNk), 0);

以下面的方式调用可以实现自动回复,自能自己看到,对方也能看到消息

       l iVar = new i(cuB, str, hC, i3, this.yXJ.ejx.cuS().fu(ctS, str));

au.Dv().a(iVar, 0);

以下面的方式暂未试验,有兴趣的小伙伴可以自己尝试调用,如果方便的话可以以反馈给我

            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);

接下来就是给上面的方法拼接参数,等会给的代码里面会提到。这里就不多重复了

 

到此我们能够回复了,那么我们回复的时机是什么呢?
于是我们需要找到什么时候收到消息,然后进行调用上面的代码

 

重复DDMS的操作进行接收消息通知的筛选 如下图

 

QQ截图20180612232854.png

 

看到筛选后关键类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]);
        }
    }
}

看着这关键字貌似挺匹配的,于是进行hook发现确实接收的时候会执行这里

 

结合上面的得到如下代码

@Override
    public void autoRepeat() {

        final Class<?> afClass = XposedHelpers.findClass("com.tencent.mm.sdk.platformtools.af", mClassLoader);
//        //获取收到消息的标记通知栏
        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);

                //这里只能自己看到回复消息 对方看不到
//                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};

                //调用这里可以实现自动回复 并发送到对方
                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");
            }
        });

    }

令我感到头痛的是在进行匹配参数,因为有些关联的引用不是很明显,找的时候有很多误区。
根据以上内容今后可以扩展聊天机器人,群助手,以及方便学习微信抢红包的流程。哈哈

以上为本课内容,我所有的内容都是以学习为主,请各位小伙伴勿要用来违法,造成的一切违法后果与本人无任何关系

垃圾代码已上传



快讯:看雪智能设备漏洞挖掘公开课招生中!

最后于 2018-6-13 08:20 被KingZd编辑 ,原因:
本主题帖已收到 1 次赞赏,累计¥5.00
最新回复 (29)
junkboy 2018-6-13 08:45
2
我记得昨晚标题不是这个,楼主清早起来卖了个萌→_→
KingZd 2018-6-13 08:53
3
junkboy 我记得昨晚标题不是这个,楼主清早起来卖了个萌→_→
你说的没错
sudami 2018-6-13 10:39
4
这里也有一个技术贴:
利用 Xposed 快速实现一个简易微信机器人

都只能自动回复文本消息吧
KingZd 2018-6-13 12:36
5
sudami 这里也有一个技术贴:利用 Xposed 快速实现一个简易微信机器人https://github.com/Blankeer/WechatBotXposed都只能自动回复文本消息吧
其余的消息类型我也会有空尝试尝试的,他们的是怎么实现我没看过,我用的办法比较笨,一步步去找到关键点进行拆解
sudami 2018-6-13 12:58
6
提供一个更简单的代码做自动文本回复 (纠正一下,硬编码的是微信6.6.1

最后于 2018-6-13 16:32 被sudami编辑 ,原因:
KingZd 2018-6-13 13:07
7
sudami 提供一个更简单的代码做自动文本回复&nbsp;
这个可以在重启手机之后不点击微信开启  进行回复吗。确实很方便,像我那样花N多时间去慢慢弄  简直不要不要了
sudami 2018-6-13 13:09
8
KingZd 这个可以在重启手机之后不点击微信开启 进行回复吗。确实很方便,像我那样花N多时间去慢慢弄 简直不要不要了
你的思路挺好啊,一步一个脚印,赞一个。
你下一步的方向可以试试调研一下“零打扰检测僵尸粉”
KingZd 2018-6-13 13:13
9
sudami 你的思路挺好啊,一步一个脚印,赞一个。 你下一步的方向可以试试调研一下“零打扰检测僵尸粉”[em_19]
嗯嗯  我会慢慢来的,谢谢大佬的鼓励。  估计按照我这个摸索模式检测出僵尸粉的时候  微信已经更新换代N多了  哈哈
Ericky 2018-6-13 13:32
10
也提供个思路665:as.CN().d(new  j(talker,  "haha",  1));
Vn小帆 2018-6-13 13:50
11
给个建议,可以把你每次做的工具做成一个合集?
zjxxhk 2018-6-13 15:18
12
这个就是聊天用吗?
KingZd 2018-6-13 15:28
13
Vn小帆 给个建议,可以把你每次做的工具做成一个合集?
目前所有的代码都丢在一起了,  没有加上控制开关  因为我这个项目还处于初期,考虑的是功能上一步一步分析自己如何去完成某项功能,帮助正在路上的盆友,功能完善后会慢慢加上控制模块,变成一个稍微正常点的应用
KingZd 2018-6-13 15:29
14
zjxxhk 这个就是聊天用吗?
这只是提供思路,具体你需要怎么去扩展。要往哪方面功能去做可以作为参考,当然如果你有建设性的意见可以  留下来  看后面阶段加上来
KingZd 2018-6-13 15:30
15
多谢大佬。
KingZd 2018-6-13 15:30
16
Ericky 也提供个思路665:as.CN().d(new j(talker, "haha", 1));
多谢大佬
roysue 2018-6-14 14:51
17
多谢大佬~
litaomn 2018-6-15 00:18
18
as.CN(),三句代码就可以搞定自动发消息,还可以指定好友,群等等,图片发送有点麻烦,不过也差不多
安卓qq 2018-6-15 02:32
19
厉害,支持大佬
gdx王 2018-6-15 16:04
20
不过是离线的,可惜了
shockc 2018-6-21 10:41
21
请教下大佬   
Class<?>  gClass  =  XposedHelpers.findClass("com.tencent.mm.kernel.g",  mClassLoader);
                                Object  g  =  XposedHelpers.callStaticMethod(gClass,  "Ea");
                                Object  filedA  =  XposedHelpers.getObjectField(g,  "fVR");
这个类和方法是怎么找到,思路是什么啊
KingZd 2018-6-22 08:56
22
shockc 请教下大佬 Class gClass = XposedHelpers.findClass("com.tencent.mm.kernel.g", mClassLoader); ...
我上面  提到的y$1里面的三种发送方式带的参数,按照参数逆推,因为我是反编译成了java  所以导入工程后,快捷键找到关联的引用进行对应的  参数方法hook,一般找到  目标执行方法后,里面不管有多少个参数  都只是按照关联去找到对应,有时候需要留意是某些参数方法不在本类里面,需要在实现的接口或者继承类里面的方法或者变量  多级查找关联
a达拉崩吧 2018-6-22 12:01
23
虽然一脸懵逼,但是大佬能把自己的思路成果分享出来,使我倍受鼓舞๑乛◡乛๑
younghare 2018-7-3 00:51
24
楼主你分析的这个微信版本是多少?
KingZd 2018-7-11 16:06
25
younghare 楼主你分析的这个微信版本是多少?
6.6.6 上传到了github
doduhuang 2018-7-31 22:33
26
我是HOOK到另外方法实现的,接收消息还是用的数据库判断,楼主能私信发个QQ么。想跟你学习一下语音的方法
xiangyutian 2018-9-26 20:33
27
谢谢LZ啊,看懂这篇文章其实只要看懂上面怎样去找到“com.tencent.mm.kernel.g”,就像LZ说的,一步一步跟一遍,代码里面的对象实例化不一定在具体调用地方。综上很受LZ大神的一点一滴写出来,辛苦了。
ViewROOT 4天前
28
请问一下 ,微信反编译出来的时候 有很多个classes.dex  您是将所有的dex都弄成java的吗?
我是马超 4天前
29
大神 能不能告诉一下 发送图片的大概思路吗

如果可以留个QQ那就更好了
我是马超 4天前
30
litaomn as.CN(),三句代码就可以搞定自动发消息,还可以指定好友,群等等,图片发送有点麻烦,不过也差不多
大佬 ,图片怎么发送 能否提供下思路和版本号
返回