[原创]Xposed第三课(微信篇) 防止好友消息撤回

KingZd 2018-5-10 22:18 3329

[原创]Xposed第二课(微信篇) 聊天界面修改文字
最近忙于工作,经过我‘咸鱼’的时间终于是有了些眉目,期间也走了许多弯路,现在做个笔记让也想学习的朋友们做个参考

接收的图 发送的图
device-2018-05-10-221244.png Screenshot_2018-05-10-21-58-26-263_com.tencent.mm.png
 

老规矩,来分析一波把
QQ截图20180510203900.png

 

刚开始没发现,看这个布局分析得到微信的聊天列表和这个聊天界面在同一层级,导致我后面hook listview的时候老是跑到首页的的列表上。

 

看到了关键点-com.tencent.mm.ui.base.MMPullDownView 在我的上篇的提到的ChattingUI$a类里面,这个才是正主。

 

那么接下来我们在看看撤回消息的时候到底发生了什么。消息是存到本地数据了的,至于为什么这么说,请继续看

com.tencent.mm.ui.chatting.ChattingUI

话说有这么个类,前面已经提到过与之关联的类ChattingUI$a 如果不知道为什么我找到后面几个关联类的请看前面的一篇帖子既可以联系起来

 

然后这个类里面没有什么实质性的东西,于是我就点开他的父类看了看

com.tencent.mm.ui.MMFragmentActivity

发现了

com.tencent.wcdb.database.SQLiteDatabase

我不说其他人看到这个会怎么想,我平时自己写聊天应用的时候也会把聊天数据存储在数据库,于是乎开始hook起来

    /**
     * 直接hook sql达到获取撤回消息id的目的
     *
     * @param applicationContext
     * @param classLoader
     */
    private void hookDB(final Context applicationContext, final ClassLoader classLoader) {
        final Class<?> sQLiteDatabase = XposedHelpers.findClass("com.tencent.wcdb.database.SQLiteDatabase", classLoader);
        final Class<?> cancellationSignal = XposedHelpers.findClass("com.tencent.wcdb.support.CancellationSignal", classLoader);
        if (sQLiteDatabase == null) return;
        XposedHelpers.findAndHookConstructor("com.tencent.wcdb.database.SQLiteProgram",
                classLoader,
                sQLiteDatabase,
                String.class,
                Object[].class,
                cancellationSignal,
                new XC_MethodReplacement() {
                    @Override
                    protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                        Object[] objArr = (Object[]) param.args[2];
                        String originalSql = param.args[1].toString();
                        //打印所有调用SQLiteProgram的sql
                        //LogUtils.e("hookDB", "sql -> " + param.args[1], "objArr:" + JSON.toJSONString(objArr));
                        if (objArr != null && originalSql.toUpperCase().startsWith("UPDATE MESSAGE")) {
                            for (Object obj : objArr) {
                                String sqlParam = obj.toString();//自己撤回10002 别人撤回10000
                                if (sqlParam.equals("10000")) {//别人撤回
                                    Object[] newObjArr = new Object[2];
                                    //param.args[1] = "UPDATE message SET type=? WHERE msgId=?";
                                    param.args[1] = "select * from message where type=? and msgId=?";
                                    param.args[2] = newObjArr;
                                    newObjArr[0] = 1;
                                    newObjArr[1] = objArr[objArr.length - 1];
                                    //param.args[1] = "UPDATE message SET content=(select (select content from message where msgId = ?)||X'0D'||X'0A'||X'0D'||X'0A'||(\"<sysmsg>wxInvoke卧槽,TA竟然要撤回上面的信息wxInvoke</sysmsg>\")),msgId=?,type=? WHERE msgId=?";
                                    LogUtils.e("hookDB", "originalSql->" + originalSql, "newSql->" + param.args[1], "sqlParam->" + JSON.toJSONString(newObjArr));
                                    WxChatInvokeMsg msg = new WxChatInvokeMsg();
                                    msg.setMsgId(newObjArr[1].toString());
                                    WxChatInvokeMsgDB.insertData(applicationContext, msg);
                                }
                            }
                        }
                        return XposedBridge.invokeOriginalMethod(param.method, param.thisObject, param.args);
                    }
                });

    }

看到上面我hook的是SQLiteProgram 会有朋友问为啥?
因为我SQLiteDatabase在里面找insert和update的方法发现里面最后走进入了SQLiteProgram 哈哈! 偷个懒我就不具体截图找给大家看了,然后把撤回的消息id记录在本地,作为后面标记的依据

接下来开始处理撤回效果了

 /**
     * 微信聊天界面
     *
     * @param applicationContext
     * @param classLoader
     */

    private void hookWxChatUIMM(final Context applicationContext, final ClassLoader classLoader) {
        XposedHelpers.findAndHookMethod("com.tencent.mm.ui.base.MMPullDownView",
                classLoader,
                "onLayout",
                boolean.class,
                int.class,
                int.class,
                int.class,
                int.class,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        ViewGroup mMPullDownView = (ViewGroup) param.thisObject;
//                        if (mMPullDownView.getVisibility() == View.GONE) return;
                        for (int i = 0; i < mMPullDownView.getChildCount(); i++) {
                            View childAt = mMPullDownView.getChildAt(i);
                            if (childAt instanceof ListView) {
                                final ListView listView = (ListView) childAt;
                                final ListAdapter adapter = listView.getAdapter();
                                XposedHelpers.findAndHookMethod(adapter.getClass(),
                                        "getView",
                                        int.class,
                                        View.class,
                                        ViewGroup.class,
                                        new XC_MethodHook() {
                                            @Override
                                            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                                                super.beforeHookedMethod(param);
                                                int position = (int) param.args[0];
                                                View view = (View) param.args[1];
                                                JSONObject itemData = null;
//                                                LogUtils.i(position, view.toString());
                                                if (position < adapter.getCount()) {
                                                    itemData = JSON.parseObject(JSON.toJSONString(adapter.getItem(position)), JSONObject.class);
                                                    int itemViewType = adapter.getItemViewType(position);
//                                                    LogUtils.i(itemViewType);
                                                    //经过以上代码可以知道    itemViewType == 1的时候打印的值是正常对话列表的值
                                                    if (itemData != null && (view != null && view.toString().contains("com.tencent.mm.ui.chatting.viewitems.p"))) {
//                                                        if (itemData != null && itemViewType == 1 && (view != null && view.toString().contains("com.tencent.mm.ui.chatting.viewitems.p"))) {
                                                        String field_msgId = itemData.getString("field_msgId");
                                                        WxChatInvokeMsg wxChatInvokeMsg = WxChatInvokeMsgDB.queryByMsgId(applicationContext, field_msgId);
                                                        ViewGroup itemView = (ViewGroup) view;
                                                        View itemViewChild = itemView.getChildAt(0);
                                                        Object tag = itemViewChild.getTag(R.id.wx_parent_has_invoke_msg);
                                                        TextView textView;
                                                        if (tag == null) {
                                                            textView = new TextView(applicationContext);
                                                            textView.setGravity(Gravity.CENTER);
                                                            textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                                                            itemViewChild.setTag(R.id.wx_parent_has_invoke_msg, textView);
                                                            textView.setId(R.id.wx_invoke_msg);
                                                            itemView.addView(textView);
                                                        } else {
                                                            textView = (TextView) itemViewChild.getTag(R.id.wx_parent_has_invoke_msg);
                                                        }
                                                        textView.setText("");
                                                        textView.setVisibility(View.GONE);
                                                        if (wxChatInvokeMsg != null) {
                                                            textView.setPadding(10, 5, 10, 5);
                                                            textView.setBackgroundDrawable(ShapeUtil.getCornerDrawable());
                                                            textView.setTextColor(Color.parseColor("#666666"));
                                                            View msgView = itemView.getChildAt(3);
                                                            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) textView.getLayoutParams();
                                                            lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
                                                            lp.addRule(RelativeLayout.BELOW, msgView.getId());
                                                            lp.bottomMargin = 50;
                                                            textView.setText("这小子想撤回这个消息");
                                                            textView.setVisibility(View.VISIBLE);
                                                            LogUtils.i(position, view, itemViewType, itemData.toJSONString());
                                                        }
                                                    }
                                                }
//

                                            }

                                        });
                                break;

                            }
                        }
                    }
                });
    }

记住hook 这个listview的时候会遇到我文章片头说的问题,导致每个item的view 对应错误,所以我加了过滤标识。

 

以上内容仅仅作为学习交流。代码很简单,乐趣在于找到这些代码位置的过程

 

垃圾代码已上传
[原创]Xposed第四课(微信篇) 朋友圈点赞 (1)



快讯:[看雪招聘]十八年来,看雪平台输出了大量安全人才,影响三代安全人才!

最后于 2018-5-22 23:00 被KingZd编辑 ,原因: 添加代码地址
最新回复 (31)
尕可 2018-5-10 23:11
2
别的不说,我先顶一个。
youxiaxy 2018-5-11 07:26
3
赞一个
你瞒我瞒 2018-5-11 18:27
4
找这个东西的过程才牛逼啊
New对象处 2018-5-11 20:08
5
用apktools找到绝望
KingZd 2018-5-11 20:38
6
New对象处 用apktools找到绝望
哈哈  我也是像你一样从绝望过来的,不过我上面提到的类都有关联提到过,你还是不好找吗?
KingZd 2018-5-11 20:39
7
你瞒我瞒 找这个东西的过程才牛逼啊
  小伙子  经历过绝望吗
New对象处 2018-5-11 20:41
8
KingZd 哈哈 我也是像你一样从绝望过来的,不过我上面提到的类都有关联提到过,你还是不好找吗?
啊?关键那时候从零入手,要是你能早点发贴我肯定不会走这么多弯路
KingZd 2018-5-11 20:45
9
New对象处 啊?关键那时候从零入手,要是你能早点发贴我肯定不会走这么多弯路
  你现在做到微信哪块了。接下来我关注点去朋友圈了
聖blue 2018-5-11 23:26
10
龙飞雪 2018-5-12 12:24
11
楼主能否,告知下,分析微信UI所使用的工具码?
jeepan 2018-5-12 15:52
12
基本用jadx,不过看着头晕,谢谢楼主
楼主发送消息的有没有研究,关键地方确实很绝望
zuoyang 2018-5-12 16:02
13
楼主能否告知下,分析微信UI所使用的工具
KingZd 2018-5-12 16:16
14
zuoyang 楼主能否告知下,分析微信UI所使用的工具
Androidkiller  Androidstudio  +  log日志打印
KingZd 2018-5-12 16:16
15
Androidkiller  Androidstudio  +  log日志打印
KingZd 2018-5-12 16:18
16
jeepan 基本用jadx,不过看着头晕,谢谢楼主 楼主发送消息的有没有研究,关键地方确实很绝望
下面准备看朋友圈了。发消息的话  应该还好把  通过布局分析得到  关键的布局信息  在微信代码里面去搜索  然后给文本框赋值  模拟按钮点击事件
红颜小学生 2018-5-12 18:24
17
大佬,xposed中怎么hook某一个应用的versionName
zzcc 2018-5-13 08:14
18
学习一下。。。
爱国敬业诚信友善 2018-5-14 20:53
19
学习了……
wx_Joe 2018-5-16 19:02
20
请问布局查看用的是什么工具呢
wdming 2018-5-16 20:03
21
KingZd 2018-5-16 22:17
22
wx_Joe 请问布局查看用的是什么工具呢
as3.0  带的layout  inspector  和  terminal  执行  adb  shell  dumpsys  activity  top
keeplooking 2018-5-18 12:12
23
哥们,你用的是微信的哪个版本的???可以一起探讨下
tDasm 2018-5-18 16:11
24
KingZd as3.0 带的layout inspector 和 terminal 执行 adb shell dumpsys activity top
楼主有空写一个xposed  hook任意类的方法并显示其入口参数及返回结果。如果参数是byte[]就显示16进制值字符串。(根据参数类型自动判断显示)
wx_Joe 2018-5-21 10:59
25
KingZd as3.0 带的layout inspector 和 terminal 执行 adb shell dumpsys activity top
我这里layout  inspector  只能看到正在开发的应用的布局,没法看第三方的应用布局。。。请问您知道为什么吗
KingZd 2018-5-21 13:45
26
wx_Joe 我这里layout inspector 只能看到正在开发的应用的布局,没法看第三方的应用布局。。。请问您知道为什么吗
原本我用layout inspector查看朋友圈的结构也不行 用Android Device Monitor里面的   可以看到布局结构  使用adb的那个命令也可以 只是看的不是很直观
wx_Joe 2018-5-21 14:34
27
某些界面进行分析会出现这个错误,可能是没有那个界面的某些控件导致的,而我这用AS自带的布局分析器没法分析微信。。。
心如芷水 2018-5-21 15:27
28
惊现高中同学,学习学习,抱紧大佬大腿。
KingZd 2018-5-22 22:43
29

是的 所以我结合三种方式去进行布局分析 我这篇介绍了我的三种方式,基本够我用了[原创]Xposed第四课(微信篇) 朋友圈点赞 (1)

最后于 2018-5-22 23:08 被KingZd编辑 ,原因:
KingZd 2018-6-13 08:12
30
jeepan 基本用jadx,不过看着头晕,谢谢楼主 楼主发送消息的有没有研究,关键地方确实很绝望
发送消息的已经贴出来了。你有在我主页进去看看
人在塔在 2018-6-22 11:02
31
jeepan 基本用jadx,不过看着头晕,谢谢楼主 楼主发送消息的有没有研究,关键地方确实很绝望
jadx载入微信之后搜索的时候总是卡死怎么办呢,按照网上的方法修改了内存大小也不行  -  -
KingZd 2018-6-22 13:23
32
人在塔在 jadx载入微信之后搜索的时候总是卡死怎么办呢,按照网上的方法修改了内存大小也不行 - -
我用as导入工程  在as里面搜索
返回