Xposed微信之发送文件

本帖最后由 randompath 于 2019-8-27 16:34 编辑

本文主要介绍通过静态分析微信发送文件的逻辑,并通过xposed实现文件自动发送。分析的微信版本为当前最新版(v7.0.6),分析工具jadx+AS。

分析过程

选择文件

点击文件,使用hierachyViewer查看选择文件界面是com.tencent.mm.pluginsdk.ui.tools.NewFileExplorerUI类。我们知道安卓Activity间发送数据主要是通过Intent,所以在NewFileExplorerUI中查找new Intent,找到以下代码

      newFileExplorerUI.wOI.a(new com.tencent.mm.ui.widget.b.c.a.b() {
            public final void bEL() {
                AppMethodBeat.i(28179);
                Intent intent = new Intent();
                intent.setClass(NewFileExplorerUI.this.getContext(), NewFileExplorerUI.class);
                intent.putExtra("explorer_mode", 1);
                intent.putStringArrayListExtra("selected_file_lst", NewFileExplorerUI.this.wOF.dxa());
                intent.putStringArrayListExtra("key_select_video_list", NewFileExplorerUI.this.wOF.dxc());
                intent.putStringArrayListExtra("CropImage_OutputPath_List", NewFileExplorerUI.this.wOF.dxb());
                intent.putExtra("GalleryUI_ToUser", NewFileExplorerUI.this.toUserName);
                NewFileExplorerUI.this.startActivityForResult(intent, 0);
                AppMethodBeat.o(28179);
            }
        });
        newFileExplorerUI.wOI.Nc(R.string.u5).a(new com.tencent.mm.pluginsdk.ui.applet.q.a() {
            public final void a(boolean z, String str, int i) {
                AppMethodBeat.i(28180);
                NewFileExplorerUI.this.hideVKB();
                if (z) {
                    Intent intent = new Intent();
                    intent.putStringArrayListExtra("selected_file_lst", NewFileExplorerUI.this.wOF.dxa());
                    intent.putStringArrayListExtra("key_select_video_list", NewFileExplorerUI.this.wOF.dxc());
                    intent.putStringArrayListExtra("CropImage_OutputPath_List", NewFileExplorerUI.this.wOF.dxb());
                    intent.putExtra("GalleryUI_ToUser", NewFileExplorerUI.this.toUserName);
                    intent.putExtra("with_text_content", str);
                    NewFileExplorerUI.this.setResult(-1, intent);
                    NewFileExplorerUI.this.finish();
                }
                AppMethodBeat.o(28180);
            }
        }).gQP.show();

猜测这两个匿名内部类至少有一个是发送button的回调,而且其回调方法的逻辑很相似。发送时如果选择的文件中有图片,会把文件与图片分开发送,所以我们猜测

  • selected_file_lst是选择的待上传文件路径
  • CropImage_OutputPath_List是选择的图片路径
  • with_text_content 是发送文件时捎带的留言

发送文件

在源码中搜索selected_file_lst关键字, 定位到类com/tencent/mm/ui/chatting/p
在文件最后找到发送代码

   static /* synthetic */ void a(p pVar, com.tencent.mm.ui.chatting.d.a aVar, int i, Intent intent) {
        AppMethodBeat.i(156138);
        if (i == -1 && intent != null) {
            int om;
            String str;
            ((aa) aVar.aU(aa.class)).g(217, i, intent);
            ArrayList stringArrayListExtra = intent.getStringArrayListExtra("selected_file_lst");
            if (aVar.dRl()) {
                om = com.tencent.mm.model.n.om(pVar.AiD);
            } else {
                om = 0;
            }
            Iterator it = stringArrayListExtra.iterator();
            while (it.hasNext()) {
                // 准备文件对象
                String str2 = (String) it.next();
                WXFileObject wXFileObject = new WXFileObject();
                // 文件路径
                wXFileObject.setFilePath(str2);
                WXMediaMessage wXMediaMessage = new WXMediaMessage();
                wXMediaMessage.mediaObject = wXFileObject;
                File file = new File(str2);
                // 文件名称
                wXMediaMessage.title = file.getName();
                // 文件大小
                wXMediaMessage.description = bo.hw(file.length());
                // 发送文件
                com.tencent.mm.pluginsdk.model.app.l.a(wXMediaMessage, "", "", pVar.AiD, 4, null);
                int lastIndexOf = file.getName().lastIndexOf(".");
                str = "";
                if (lastIndexOf >= 0 && lastIndexOf < file.getName().length() - 1) {
                    str = file.getName().substring(lastIndexOf + 1);
                }
                h hVar = h.rdc;
                Object[] objArr = new Object[5];
                objArr[0] = Long.valueOf(file.length());
                objArr[1] = Integer.valueOf(0);
                objArr[2] = Integer.valueOf(aVar.dRl() ? 1 : 0);
                objArr[3] = Integer.valueOf(om);
                objArr[4] = str;
                hVar.e(14986, objArr);
            }
            str = intent.getStringExtra("with_text_content");
            if (!bo.isNullOrNil(str)) {
                com.tencent.mm.plugin.messenger.a.g.bWU().ft(str, pVar.AiD);
            }
        }
        AppMethodBeat.o(156138);
    }

在准备文件对象时只有bo.hw(file.length())的含义不明,跟进去发现这个方法将文件大小转为字符串,简单翻译后代码如下

  private String getFileDesc(long filesize) {
        long j = filesize;
        if ((j >> 30) > 0) {
            // GB
            return (((double) Math.round((((double) j) * 10.0d) / 1.073741824E9d)) / 10.0d) + " GB";
        }
        if ((j >> 20) > 0) {
            // MB
            return (((double) Math.round((((double) j) * 10.0d) / 1048576.0d)) / 10.0d) + " MB";
        }
        if ((j >> 9) <= 0) {
            return j + " B";
        }
        return (((double) Math.round((((double) j) * 10.0d) / 1024.0d)) / 10.0d) + " KB";

    }

com.tencent.mm.pluginsdk.model.app.l.a(wXMediaMessage, "", "", pVar.AiD, 4, null); 就是发送文件的调用。发送文件需要两个必要参数:文件对象与接收者的微信id,所以猜测pVar.AiD就是接收方的微信id,对于发送对象可以通过反射构建。分析到这里就可以了,接下来试着编写发送文件的代码

public class AttachSendingHook {
    Class IMediaObject;
    Class mediaMsgSenderClz, fileObjectClz, mediaMessageClz;

    public AttachSendingHook(XC_LoadPackage.LoadPackageParam lpparam) {
        ClassLoader cl = lpparam.classLoader;
        IMediaObject = XposedHelpers.findClass("com.tencent.mm.opensdk.modelmsg.WXMediaMessage$IMediaObject", cl);
        fileObjectClz = XposedHelpers.findClass("com.tencent.mm.opensdk.modelmsg.WXFileObject", cl);
        mediaMessageClz = XposedHelpers.findClass("com.tencent.mm.opensdk.modelmsg.WXMediaMessage", cl);
        mediaMsgSenderClz = XposedHelpers.findClass("com.tencent.mm.pluginsdk.model.app.l", cl);
    }

    public void sendAttach(String recvr, String filePath) {
        File file = new File(filePath);
        if (file.exists() && file.isFile()) {
            debug("向 %s 发送文件: %s", recvr, filePath);
            Object fileObject = XposedHelpers.newInstance(fileObjectClz);
            XposedHelpers.callMethod(fileObject, "setFilePath", filePath);
            Object mediaObject = XposedHelpers.newInstance(mediaMessageClz);
            XposedHelpers.setObjectField(mediaObject, "mediaObject", IMediaObject.cast(fileObject));
            XposedHelpers.setObjectField(mediaObject, "title", file.getName());
            XposedHelpers.setObjectField(mediaObject, "description", getFileDesc(file.length()));
            // com.tencent.mm.pluginsdk.model.app.l.a(wXMediaMessage, "", "", pVar.xVs, 4, null);
            XposedHelpers.callStaticMethod(mediaMsgSenderClz, "a", mediaObject, "", "", recvr, 4, null);
        } else {
            error("文件不存在,无法发送 %s", filePath);
        }
    }

    private String getFileDesc(long filesize) {
        long j = filesize;
        if ((j >> 30) > 0) {
            // GB
            return (((double) Math.round((((double) j) * 10.0d) / 1.073741824E9d)) / 10.0d) + " GB";
        }
        if ((j >> 20) > 0) {
            // MB
            return (((double) Math.round((((double) j) * 10.0d) / 1048576.0d)) / 10.0d) + " MB";
        }
        if ((j >> 9) <= 0) {
            return j + " B";
        }
        return (((double) Math.round((((double) j) * 10.0d) / 1024.0d)) / 10.0d) + " KB";

    }
}

测试发送成功,效果如下

对于发送文件的分析思路大体就是这样,而发送图片与短视频的分析过程与此类似,这里就不再赘述,有兴趣的自己研究下吧。

THE END
喜欢就支持以下吧
点赞0
分享
评论 抢沙发
  • 管埋员

    昵称

  • 取消
    昵称