Xposed-微信自动加好友1

搜索好友显示好友信息

Posted by JovenHe on February 18, 2019

前两天有人想让我做一个微信自动加好友的功能,今天就想试一试。 以微信6.6.7版本为例,手机root与Xposed框架安装本文不做讨论,如有需要查看论坛内其他帖子,本文只用于Xposed模块编写。

严重声明 本文的意图只有一个就是通过分析app学习更多的逆向技术,如果有人利用本文知识和技术进行非法操作进行牟利,带来的任何法律责任都将由操作者本人承担,和本文作者无任何关系,最终还是希望大家能够秉着学习的心态阅读此文。

经坛友提醒,使用文本存储手机号,在进行读取for循环搜索好友时,10个以上的时候就会产生操作过于频繁的提醒,而且这个提醒大概一个小时才能解除。 经过优化后,增加了线程的sleep时间,可实现自动循环查看好友信息,但在运行到25个左右时也会触发操作过于频繁的提醒。 并且如果加好友的功能也增加上的话,也会由于加好友频繁而被限制,所以最好是通过ContentProvider把大量手机号批量写入到手机通讯录数据库中,然后使用微信的添加通讯录好友的方式来添加好友,以下分析流程就仅做参考吧。

1.首先查看微信加好友的页面

使用 当前Activity 这个app来查看,是FTSAddFriendUI这个Activity。

2.使用jadx打开app,查找FTSAddFriendUI

FTSAddFriendUI

3.寻找突破点

大体浏览后没有类似EditView的实例对象,但发现一个内部类FTSAddFriendUI$5

内部类FTSAddFriendUI$5.png

-1通过简单分析

确定这个内部类是搜索好友后的结果显示,成功的话就intent跳转,失败的话就显示该用户不存在等错误信息

-2直接查找它的使用

是CM方法使用到它了,分析可得这个方法的参数就是输入框的字符串,字符串不为空后进行查询,并显示一个正在查询的Dialog,有结果后回调OnCancelListener,并且触发内部类FTSAddFriendUI$5中的结果显示方法。

CM方法.png

Hook CM方法,打印string参数,发现就是输入框的文本数据,确认CM方法就是要找的方法

-3xposed直接使用

final Class FTSAddFriendUIClass=loadPackageParam.classLoader.loadClass("com.tencent.mm.plugin.fts.ui.FTSAddFriendUI");
                     findAndHookMethod(FTSAddFriendUIClass,
                            "onCreate",Bundle.class,
                            new XC_MethodHook() {
                                @Override
                                protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
                                    super.afterHookedMethod(param);
                                   new Handler().postDelayed(new Runnable() {
                                       @Override
                                       public void run() {
                                           XposedHelpers.callMethod(param.thisObject,"CM","微信号/QQ号/手机号");
                                       }
                                   },1000);
                                }
                            });

微信显示效果:

搜索好友显示失败.gif

-4解决问题

结果可以看到只有dialog,dialog结束后并没有反应,但dialog能显示说明代码执行了,那就来看一下FTSAddFriendUI$5这个结果处理类里的方法有没有执行吧。

findAndHookMethod("com.tencent.mm.plugin.fts.ui.FTSAddFriendUI$5",loadPackageParam.classLoader,
       "a",int .class, int .class,String.class,XposedHelpers.findClass("com.tencent.mm.ab.l",loadPackageParam.classLoader) ,
       new XC_MethodHook() {
           @Override
           protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
               super.beforeHookedMethod(param);
 
               log("FTSAddFriendUI$5--0="+param.args[0]+"=1="+param.args[1]+
                       "=2="+param.args[2]);
           }
            
       });

打印结果如下:

2019-02-18 17:55:19.347 3872-3872/? I/Xposed: [17:55:19]: FTSAddFriendUI$5–0=0=1=0=2=Everything is OK

搜索结果处理的方法执行了,说明问题不在CM方法,而是在处理方法中因为某个问题而停止了。 那就来分析一下这个a方法里的哪段代码是有问题的吧。

a方法完整代码如下:

public final void mo2331a(int i, int i2, String str, C0879l c0879l) {
           C1261g.m2963DF().mo8629b(106, (C0874e) this);
           FTSAddFriendUI.this.aQA();
           if (i == 0 && i2 == 0) {
               FTSAddFriendUI.this.iJw = ((C9339f) c0879l).bcS();
               if (FTSAddFriendUI.this.iJw.rHb > 0) {
                   if (FTSAddFriendUI.this.iJw.rHc.isEmpty()) {
                       C35785h.m67602a(FTSAddFriendUI.this, C8733g.search_contact_not_found, 0, true, null);
                       return;
                   }
                   Intent intent = new Intent();
                   intent.putExtra("add_more_friend_search_scene", 3);
                   if (FTSAddFriendUI.this.iJw.rHc.size() > 1) {
                       try {
                           intent.putExtra("result", FTSAddFriendUI.this.iJw.toByteArray());
                           C36379d.m70344b(FTSAddFriendUI.this.mController.tml, "subapp", ".ui.pluginapp.ContactSearchResultUI", intent);
                           return;
                       } catch (Throwable e) {
                           C3327x.printErrStackTrace("MicroMsg.FTS.FTSAddFriendUI", e, "", new Object[0]);
                           return;
                       }
                   }
                   ((C33521h) C1261g.m2977l(C33521h.class)).mo24028a(intent, (biy) FTSAddFriendUI.this.iJw.rHc.getFirst(), FTSAddFriendUI.this.jvZ);
               }
               FTSAddFriendUI.this.jvX = 1;
               FTSAddFriendUI.m48692g(FTSAddFriendUI.this);
           } else {
               switch (i2) {
                   case DownloadResult.CODE_CONNECTION_EXCEPTION /*-24*/:
                       C29477a eV = C29477a.m50825eV(str);
                       if (eV == null) {
                           FTSAddFriendUI.this.jvQ.setText(C8733g.no_contact_result);
                           break;
                       } else {
                           FTSAddFriendUI.this.jvQ.setText(eV.desc);
                           break;
                       }
                   case -4:
                       if (i != 4) {
                           FTSAddFriendUI.this.jvQ.setText(FTSAddFriendUI.this.getString(C8733g.search_contact_err_no_code));
                           break;
                       }
                   default:
                       FTSAddFriendUI.this.jvQ.setText(C8733g.no_contact_result);
                       break;
               }
               FTSAddFriendUI.this.jvX = -1;
               FTSAddFriendUI.this.jvY = 1;
           }
           FTSAddFriendUI.m48693h(FTSAddFriendUI.this);
       }

因为上面打印log时前两个参数都是0,所以精简后如下:

public final void mo2331a(int i, int i2, String str, C0879l c0879l) {
    C1261g.m2963DF().mo8629b(106, (C0874e) this);
    FTSAddFriendUI.this.aQA();
    if (i == 0 && i2 == 0) {
        FTSAddFriendUI.this.iJw = ((C9339f) c0879l).bcS();
        if (FTSAddFriendUI.this.iJw.rHb > 0) {
            if (FTSAddFriendUI.this.iJw.rHc.isEmpty()) {
                C35785h.m67602a(FTSAddFriendUI.this, C8733g.search_contact_not_found, 0, true, null);
                return;
            }
            Intent intent = new Intent();
            intent.putExtra("add_more_friend_search_scene", 3);
            if (FTSAddFriendUI.this.iJw.rHc.size() > 1) {
                try {
                    intent.putExtra("result", FTSAddFriendUI.this.iJw.toByteArray());
                    C36379d.m70344b(FTSAddFriendUI.this.mController.tml, "subapp", ".ui.pluginapp.ContactSearchResultUI", intent);
                    return;
                } catch (Throwable e) {
                    C3327x.printErrStackTrace("MicroMsg.FTS.FTSAddFriendUI", e, "", new Object[0]);
                    return;
                }
            }
            ((C33521h) C1261g.m2977l(C33521h.class)).mo24028a(intent, (biy) FTSAddFriendUI.this.iJw.rHc.getFirst(), FTSAddFriendUI.this.jvZ);
        }
        FTSAddFriendUI.this.jvX = 1;
        FTSAddFriendUI.m48692g(FTSAddFriendUI.this);
    }
    FTSAddFriendUI.m48693h(FTSAddFriendUI.this);
}

前面代码分析后无问题,但 if (FTSAddFriendUI.this.iJw.rHb > 0) 判断时不知rHb的值,此处需要hook一下。

findAndHookMethod("com.tencent.mm.plugin.fts.ui.FTSAddFriendUI$5",loadPackageParam.classLoader,
    "a",int .class, int .class,String.class,XposedHelpers.findClass("com.tencent.mm.ab.l",loadPackageParam.classLoader) ,
    new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
        log("FTSAddFriendUI$5--0="+param.args[0]+"=1="+param.args[1]+
        "=2="+param.args[2]+"=rHb="+XposedHelpers.findField(
            XposedHelpers.findClass("com.tencent.mm.protocal.c.bja",loadPackageParam.classLoader),"rHb").
            get(XposedHelpers.callMethod((param.args[3]),"bcS")));
    }

打印得知rHb=0,所以不会走if语句内的代码,只有两个方法m48692g与m48693h有可能有问题,按顺序检查参数

最终发现是这两个方法中使用了FTSAddFriendUI的变量bWm,而在输入框使用addTextChangedListener进行绑定后,bWm存储了输入的字符串,所以如果直接调用CM方法的话,导致bWm变量为空,所以解决方法如下:

final Class FTSAddFriendUIClass=loadPackageParam.classLoader.loadClass("com.tencent.mm.plugin.fts.ui.FTSAddFriendUI");
findAndHookMethod(FTSAddFriendUIClass,"onCreate",Bundle.class,
    new XC_MethodHook() {
        @Override
        protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    XposedHelpers.setObjectField(param.thisObject,"bWm","微信号/QQ号/手机号");
                    XposedHelpers.callMethod(param.thisObject,"CM","微信号/QQ号/手机号");
                }},1000);
    }
});

显示如下:

搜索好友显示成功.gif

当然如果输入数据搜索不到好友,结果处理方法的参数是 2019-02-18 17:58:34.082 3872-3872/? I/Xposed: [17:58:34]: FTSAddFriendUI$5–0=4=1=-4=2=User do not exist

以此可以实现用户是否存在的判断。