Android微信逆向--实现发朋友圈动态

news2024/12/22 9:18:29

Android微信逆向--实现发朋友圈动态 

0x0 前言#

最近一直在研究Windows逆向的东西,想着快要把Android给遗忘了。所以就想利用工作之余来研究Android相关的技术,来保持对Android热情。调用微信代码来发送朋友圈动态一直是自己想实现的东西,研究了一下,果然实现了,遂写下本文当作记录。本文主要分析发送纯文字朋友圈动态和发送图片朋友圈动态。

0x1 朋友圈动态类型分析#

本文用到的工具如下:

  • PC
  • 一台可以调试微信进程的Android手机
  • 微信7.0.11
  • ddms(用于跟踪调用过程)
  • uiautomatorviewer(用于定位控件id)
  • jadx(用于对微信apk静态分析)
  • frida(用于hook微信,获得相关信息,发布朋友圈)

在分析代码之前,首先要定位到与之相近的地方,我们首先想到的肯定是发朋友圈动态那个界面,如何查看发朋友圈动态的界面是哪个Activity呢?很简单,先在手机上打开发表朋友圈动态的界面

把手机连接电脑,打开USB调试,在PC的cmd窗口中执行以下命令:

adb shell dumpsys activity top

可以看到发朋友圈动态的Activity就是com.tencent.mm.plugin.sns.ui.SnsUploadUI

虽然找到了Activity,但还是不能高兴太早,想要通过Activity知道哪部分是发朋友圈的动态代码还是比较费力的。于是我们就想到从“发表”按钮入手,找出发表朋友圈动态的相关代码。点击“发表”按钮会发生什么?发表是一个动态的行为,我们可以通过跟踪点击“发表”按钮时的调用过程,来找到有用的信息。跟踪调用过程,可以使用ddms工具来完成。打开ddms,选中微信进程,在手机中打开发表朋友圈界面,然后在ddms中点击下图圈出的图标开始跟踪:

将朋友圈动态发出,再点一次上图圈出的图标停止跟踪。ddms会生成跟踪结果,对于跟踪结果,怎么找到按钮事件相关的信息呢,学过Android的朋友就会想到onClick方法,那我们就在ddms的搜索结果中搜索这个名称:

成功的定位到了onClick的位置,但是比起这条onClick结果,更加令人引人注目的是它的上一条结果,因为它包含了我们刚才找到的Activity的类名:

知道这个方法被调用,我们去看看com.tencent.mm.plugin.sns.ui.SnsUploadUI类里的OnMemuItemClick究竟是什么。

用jadx打开微信apk,定位到com.tencent.mm.plugin.sns.ui.SnsUploadUI类,在类中搜索onMemuItemClick,结果不多,看起来比较像的就是这个onMemuItemClick了:

在onMemuItemClick方法中看到了:

String unused2 = SnsUploadUI.this.desc = SnsUploadUI.this.tQN.getText().toString();

这行代码有什么特别的呢,在我看来,有两个特别的地方:

  • desc是描述(description)的英文单词的缩写
  • this.desc被赋予this.tQN.getText().toString()

我们发朋友圈动态时候,是要写动态的描述的,所以这个desc可能就是发朋友圈动态的描述,如果是描述,我们就可以根据这个描述,顺藤摸瓜找到发朋友圈动态的地方。而且this.desc的值又来自于this.tQN.getText().toString(),即this.tQN很有可能就是我们填写动态描述的文本框。我们来看看this.tQN赋值的地方,它在onCreate方法被赋值:

可以知道它的id是d41,那么d41是哪个控件?打开uiautomatorviewer,对发朋友圈界面截图分析,点击截图中的文本框,uiautomatorviewer右侧跳转到了相应的位置,果然,d41就是发动态时填写描述文本框的id

好了,现在知道this.desc就是发表朋友圈动态时的描述,跟上他应该就可以找到发朋友圈动态的地方。继续往onMemuItemClick方法下部分析,可以看到this.desc被传入了SnsUploadUI.this.tQO.a方法:

SnsUploadUI.this.tQO.a方法定义在接口com.tencent.mm.plugin.sns.ui.z中:

知道它定义在哪个接口并不能解决问题,毕竟接口没有实质性代码,要找还得找接口的的实现类,在com.tencent.mm.plugin.sns.ui.SnsUploadUI类中寻找this.tQO在哪里会被赋值。最终,我们在com.tencent.mm.plugin.sns.ui.SnsUploadUI类的ag方法中看到了许多给this.tQO赋值的地方:

由此,可见this.tQO被赋予什么值是根据this.tMY来决定的,this.tMY是一个int类型的数据,那我们hook com.tencent.mm.plugin.sns.ui.SnsUploadUI类的ag方法就可以知道this.tMY是什么值。在这里,我用frida来hook,frida的javascript部分代码如下:

var SnsUploadUI= Java.use('com.tencent.mm.plugin.sns.ui.SnsUploadUI');
var ag = SnsUploadUI.ag.overload("android.os.Bundle");
//get sns type
ag.implementation=function(bundle){
    var ret = ag.call(this,bundle);
    send("sns type = " + this.tMY.value);
    return ret;
}

hook之后,每当我们在手机上打开发布朋友圈动态的界面,ag方法被调用,控制台就会输出相应的数字。经过我的测试,这个数字是发表朋友圈动态的类型。朋友圈类型和其对应类如下:

  • 0 带图片的动态,对应:com.tencent.mm.plugin.sns.ui.ai
  • 9 纯文字动态,对应:com.tencent.mm.plugin.sns.ui.ae

这些类都直接或间接的实现了上面讲到的com.tencent.mm.plugin.sns.ui.z接口。这样一来,就知道this.tQO会根据朋友圈的动态类型进行初始化,那么,上面的SnsUploadUI.this.tQO.a方法很有可能就是发朋友圈动态的方法。接下来,我们根据不同的朋友圈动态所对应的类来分别分析

0x2 文字动态分析#

文字动态分析起来应该比图片动态来说简单一些,我们就先来分析它。上面讲到,这类动态对应类是com.tencent.mm.plugin.sns.ui.ae,这个类里我们主要看a方法,在看a方法之前,先看它传入什么参数,为了看清楚,这就要回看上文onMenuItemClick方法调用a方法的地方:

public final boolean onMenuItemClick(MenuItem menuItem) {
    String unused2 = SnsUploadUI.this.desc = SnsUploadUI.this.tQN.getText().toString();
    int pasterLen = SnsUploadUI.this.tQN.getPasterLen();
    int privated = SnsUploadUI.this.tKm.getPrivated();
    int syncFlag2 = SnsUploadUI.this.tKm.getSyncFlag();
    ......
    PInt pInt = new PInt();
    if (SnsUploadUI.this.tQO instanceof a) {
        Bundle bundle = new Bundle();
        bundle.putInt("param_is_privated", privated);
        bundle.putString("param_description", SnsUploadUI.this.desc);
        bundle.putStringArrayList("param_with_list", new ArrayList(SnsUploadUI.this.uij.getAtList()));
        bundle.putInt("param_paste_len", pasterLen);
        try {
            bundle.putByteArray("param_localtion", SnsUploadUI.this.uik.getLocation().toByteArray());
        } catch (IOException e2) {
            ab.printErrStackTrace("MicroMsg.SnsUploadUI", e2, "parse location error", new Object[0]);
        }
        bundle.putBoolean("param_is_black_group", SnsUploadUI.this.tQS);
        bundle.putStringArrayList("param_group_user", SnsUploadUI.this.tQR);
        bundle.putInt("param_contact_tag_count", SnsUploadUI.this.tOk);
        bundle.putInt("param_temp_user_count", SnsUploadUI.this.tOl);
        pInt.value = ((a) SnsUploadUI.this.tQO).getContentType();
        z unused4 = SnsUploadUI.this.tQO;
    } else {
        SnsUploadUI.this.tQO.a(privated, syncFlag2, SnsUploadUI.this.tKm.getTwitterAccessToken(), SnsUploadUI.this.desc, SnsUploadUI.this.uij.getAtList(), SnsUploadUI.this.uik.getLocation(), (LinkedList<Long>) null, pasterLen, SnsUploadUI.this.tQS, SnsUploadUI.this.tQR, pInt, SnsUploadUI.this.tOj, SnsUploadUI.this.tOk, SnsUploadUI.this.tOl);
    }
}

这就是a方法调用的地方,根据这段代码和编写hook a方法的代码来推出它的参数。hook代码如下:

var ae = Java.use('com.tencent.mm.plugin.sns.ui.ae');
var ae_a = ae.a.overload("int","int","org.b.d.i","java.lang.String","java.util.List","com.tencent.mm.protocal.protobuf.bdi","java.util.LinkedList","int","boolean","java.util.List","com.tencent.mm.pointers.PInt","java.lang.String","int","int");
ae_a.implementation = function(isPrivate,syncFlag2,twitterAccessToken,desc,atList,location,list1,pasterLen,bool1,list2,pint1,str1,num1,num2){
    var ret = ae_a.call(this,isPrivate,syncFlag2,twitterAccessToken,desc,atList,location,list1,pasterLen,bool1,list2,pint1,str1,num1,num2);
    console.log("************Basic Info************");
    console.log("isPrivate = " + isPrivate);
    console.log("syncFlag2 = " + syncFlag2);
    console.log("twitterAccessToken = " + twitterAccessToken);
    console.log("desc = " + "'" + desc + "'");
    if(atList.size()>0){
        for(var i=0;i<atList.size();i++){
            console.log("atList[" + i + "] = " + atList.get(0));
        }
    }
    if(location != null){
         
        if(location.yRD.value != null){
            console.log("location.yRD = " + location.yRD.value);
        }

        if(location.yRE.value != null){
            console.log("location.yRE = " + location.yRE.value);
        }

    }
    console.log("list1 = " + list1);
    console.log("pasterLen = " + pasterLen);
    console.log("bool1 = " + bool1);
    if(list2 != null){
        console.log("list2 = " + list2.size());
    }
    else{
        console.log("list2 = " + list2);
    }
    console.log("pint1 = " + pint1.value.value);
    console.log("str1 = " + str1);
    console.log("num1 = " + num1);
    console.log("num1 = " + num1);

    return ret;
}//ae.a

hook成功后,发一条纯文字的朋友圈动态,打印出:

所以,可得:

- privated(int):动态是否私密:0公开,1私密
- desc(String):朋友圈的文本
- AtList(List<String>):艾特人的wxid
- Location(com.tencent.mm.protocal.protobuf.bdi):定位信息

好多参数我们不知道是什么,不过问题不大,那些我们需要的参数已经能搞懂了。懂得a方法的参数,那能否尝试直接调用它?先来看一下com.tencent.mm.plugin.sns.ui.ae类的构造函数能否调用:

构造函数有Activity类型的参数,Activity类型的参数是很难构造的,所以放弃构造com.tencent.mm.plugin.sns.ui.ae类来调用a方法。那我们直接去看a方法,看能不能找到有用的东西。由于是文字动态,所以我们着重关注传入的文本,即com.tencent.mm.plugin.sns.ui.SnsUploadUI类的desc成员,在a方法中它是第4个参数:

可见this.desc在a方法中它是str,而且在a方法中,str只有一处引用:

str传给了ayVar.adk()方法,找一下ayVar来自哪里,它在a方法里初始化,而且初始化方式很简单:

只传入一个数字就能初始化,我们初始化ay类的时候不用深究这个数字是什么,和它传入一样的值即可。在a方法的尾部,还看到一个引人注目的commit方法:

猜测这就是发布朋友圈的方法,写个简单的frida脚本来验证一下:

if(Java.available)
{
    Java.perform(function(){
        var ay_class = Java.use("com.tencent.mm.plugin.sns.model.ay");
        var desc = "To be, or not to be, that is a question.";
        var ayInstance = ay_class.$new(2);
        ayInstance.adk(desc);
        ayInstance.commit();
    });
}

文字动态的内容是:To be, or not to be, that is a question.。果不其然,脚本运行后,文字动态发表成功了

经过对com.tencent.mm.plugin.sns.ui.ae的a方法的分析,我们可以知道,a方法主要传入一些发朋友圈动态所需要的通用的数据,比如动态是否私密,动态的文字描述,艾特的人,定位信息等,这些信息在其他类型的朋友圈动态中也会用得到。我们还知道发文字动态只需要文字描述就能发表成功。

0x3 带图片的朋友圈动态分析#

带图片的的动态和文字动态差不多,只是多加了图片的参数,我们在分析此类动态时多关注图片在哪传入即可。带图片的动态对应的类是com.tencent.mm.plugin.sns.ui.ai,有了上面的经验,我们直接去看它的a方法(ai类有许多a方法,注意这里说的a方法参数和com.tencent.mm.plugin.sns.ui.z接口里的a方法参数一致)。在a方法的开头,看到利用迭代器去遍历一个列表,遍历过程中组装com.tencent.mm.plugin.sns.data.j类的数据,然后把j类放入链表linkedList2中:

在组装数据的时候,我们看到j类构造时传入一个字符串和数字,而这个字符串对应j类的path字段,这可能就是图片的路径:

那么我们猜测j类就是存储朋友圈的动态图片信息的类,上面提到j类被放入链表linkedList2中,那么来看linkedList2被哪里引用了

看到醒目的字符串:commit pic size,这应该是日志要打印的字符串,现在基本上可以确定j类就是存储要发表的图片的信息的类了,那么linkedList2就是存储所有将要发表的图片信息,继续往下寻找linkedList2还被哪里引用了

可以看到linkedList2传入两个地方,一处传入a方法:

另一处传入com.tencent.mm.plugin.sns.ui.ai$a类构造函数:

linkedList2传入a类后,又赋值给成员变量tPF,这个tPF成员变量只在a类的dU方法中被引用

而dU方法在哪里调用呢?在a类的父类:com.tencent.mm.plugin.sns.model.h中,我们看到dU方法在u方法被调用:

而u方法在ai类的a方法中调用(可以回看前面的图)。分析到这,上面的linkedList2传出去之后都终有所属了,即最终都传入了com.tencent.mm.plugin.sns.model.ay类ey方法。知道图片往哪传了,就写段frida代码调用试试吧

if(Java.available)
{
    Java.perform(function(){
        var ay_class = Java.use("com.tencent.mm.plugin.sns.model.ay");
        var j_class = Java.use("com.tencent.mm.plugin.sns.data.j")
        var desc = "To be, or not to be, that is a question.";
        var likedList_class = Java.use("java.util.LinkedList");
        var linkedListInstance = likedList_class.$new();
        var ayInstance = ay_class.$new(1);
        var jInstance1 = j_class.$new("/storage/emulated/0/test1.jpg",2);
        var jInstance2 = j_class.$new("/storage/emulated/0/test2.jpg",2);
        var jInstance3 = j_class.$new("/storage/emulated/0/test3.jpg",2);
        
        linkedListInstance.add(jInstance1);
        linkedListInstance.add(jInstance2);
        linkedListInstance.add(jInstance3);
        ayInstance.ey(linkedListInstance);
        ayInstance.adk(desc);
        ayInstance.commit();
    });
}

上面的代码在发送文本动态代码的基础上初始化三个j类,分别传入三个本地图片路径,再将三个类实例添加到链表,再将链表传入ay类的ey方法,最后调用ay类的commit方法将动态发送出去,代码运行,发现带图片的朋友圈动态发表成功:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Internet Download Manager 逆向分析

写在前面 文章仅供学习&#xff0c;切勿用于商业用途&#xff0c;出于版权原因&#xff0c;文章不提供资源下载。 论坛上较早之前已经有前辈对IDM的序列号算法进行过逆向分析 以及最近有师傅尝试对本篇文章对象相同的版本进行了逆向分析&#xff0c;但是比较遗憾的是该文章并…

瑞萨e2studio(26)----SPI驱动TFT-LCD屏

瑞萨e2studio.26--SPI驱动TFT-LCD屏 概述视频教学csdn课程样品申请完整代码下载屏幕接口接线方式新建工程工程模板保存工程路径芯片配置工程模板选择时钟配置开始SPI配置SPI属性配置IO配置头文件定义回调函数lcd_init.clcd.c设置区域颜色显示字符串显示汉字显示图片结果演示 概…

点击查看详情 | 网页版微信客户管理系统如何操作试用?

微信作为我们日常生活中最常用的社交应用之一&#xff0c;早已成为我们与朋友、家人和同事保持联系的重要工具&#xff0c;也是营销引流的重要平台。 通过微信营销&#xff0c;可以比较精准定向亲近用户。而微信的功能并没有很能满足做微信营销的人群&#xff0c;所以我们需要借…

S5PV210裸机(五):定时器

本文主要探讨210定时器相关知识&#xff0c;210定时器主要包含PWN定时器&#xff0c;系统定时器&#xff0c;看门狗&#xff0c;RTC。 PWM定时器 210有5个PWM定时器,timer0、1、2、3通过对应PIO产生PWM波形信号并输出,timer4没有GPIO只产生内部定时器中断 PWM…

Python 安装CSF(布料模拟滤波)的环境配置

一、环境配置 1.1 下载源码: Github下载CSF库源码 1.2 解压文件如下: 二、安装CSF库 2.1在解压文件中找到python文件夹所在目录 2.2 输入cmd并回车,来打开终端窗口 2.3激活虚拟环境 通过: activate +你的虚拟环境名称。来激活安装CSF库的虚拟环境。【不执行此

k8s镜像加devops

展示 1.配套资料2.devops 3.elk日志收集 4.grafana监控 5.dashboard![在这里插入图片描述](https://img-blog.csdnimg.cn/bf294f9fd98e4c038858a6bf5c34dbdc.png 目的 学习k8s来来回回折腾很久了&#xff0c;光搭个环境就能折腾几天。这次工作需要终于静下心来好好学习了一…

【USMA】N1CTF2022-praymoon

前言 本题主要利用 USMA 解题&#xff0c;当然还有其他做法&#xff0c;暂时不表 程序分析 启动脚本就不看了&#xff0c;该开的保护都开了。看下文件系统初始化脚本&#xff1a; #!/bin/shmkdir /tmp mount -t proc none /proc mount -t sysfs none /sys mount -t devtmpf…

codeforces (C++ Haunted House)

题目&#xff1a; 翻译&#xff1a; 思路&#xff1a; 1、由题目可知&#xff0c;他想让我们判断交换相邻字符位置后将二进制转为十进制后&#xff0c;能否整除2的次方。能整除即输出需要交换的次数&#xff0c;不能则输出-1。&#xff08;例&#xff1a;输入3和010这组数据就…

二分查找:如何快速定位IP对应的省份地址?

文章来源于极客时间前google工程师−王争专栏。 通过IP地址查找IP归属地功能&#xff1a; 这个功能是通过维护一个很大的IP地址库来实现。地址库中包含IP地址范围和归属地的对应关系。 当我们查询202.201.133.13这个IP地址归属地时&#xff0c;在地址库中搜索&#xff0c;这个…

真实感受:是智能家居在选择合适的技术!

科技从来都是为了让我们的生活更加的简单、舒适&#xff0c;而智能家居的智能&#xff0c;体现在如何更更更方便的使用我需要控制的家居。 例如&#xff1a;下班躺在床上想休息&#xff0c;房间和大厅的灯还开着&#xff0c;这时你会选择什么产品躺着解决问题&#xff1f; 红外…

【MySQL】逻辑架构

逻辑架构 逻辑架构剖析服务器处理客户端请求连接层服务层SQL Interface : SQL接口Parser : 解析器Optimizer : 查询优化器Caches&Buffers : 查询缓存组件 引擎层存储层 SQL执行流程MySQL查询流程查询缓存解析器词法分析语法分析 优化器执行器 数据库缓冲池 逻辑架构剖析 服…

云安全—责任共担

0x00 前言 云安全的职责范围实际上一直遵循的是&#xff0c;谁提供谁负责&#xff0c;如果交付给云消费者的时候&#xff0c;交付者使用过程中就要自行负责&#xff0c;也就是我们经常遇到的配置不当等问题&#xff0c;在三层服务模式中&#xff0c;责任互相嵌套&#xff0c;最…

软件测试(五)自动化 selenium

文章目录 自动化测试单元测试&#xff1a;单元测试&#xff1a;UI自动化 selenium工具定义特点&#xff1a;原理&#xff1a;seleniumjava环境搭建SeleniumAPI获取测试结果&#xff1a;添加等待浏览器操作键盘事件鼠标事件多层框架/窗口定位下拉框处理弹窗处理上传文件操作关闭…

10种常用基础模块电路,电子控制不再是难题!

你是否曾经为电子控制中的复杂电路而烦恼&#xff1f; 现在&#xff0c;我将向你展示10个最具实用性和普遍性的模块电路图&#xff0c;让你轻松掌握电子控制的核心技术&#xff01; 这些电路图不仅简单易懂&#xff0c;而且非常具有趣味性&#xff1a; 1、RS232通讯电路&…

Java面试——RPC协议

涉及到分布式方面知识的话&#xff0c;RPC协议是逃不开的&#xff0c;所以在此记录一下RPC协议。 什么是RPC协议 RPC协议&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;简单的来说&#xff1a;RPC协议是一种通过网络从远程计算机程序获取服务的协议…

【unity小技巧】实现无限滚动视图和类似CSGO的开箱抽奖功能及Content Size Fitter组件的使用介绍

文章目录 一篇一句前言素材一、无限滚动视图1. 绘制视图2. Content Size Fitter是布局控件&#xff08;1&#xff09;在文本框中使用&#xff08;2&#xff09;控制Scroll View(Scroll Rect组件)控件下Content的大小 3. 控制视图无限滚动4. 向右拉无限滚动5. 修复滚动视图一卡一…

julia笔记:字符和字符串

1 字符 Char类型的值代表单个字符 ca #a: ASCII/Unicode U0061 (category Ll: Letter, lowercase)typeof(c) #Char 将 Char 转换为其对应的整数值&#xff0c;即 Unicode 代码 cInt(c) c #97typeof(c) #Int64 将一个整数值&#xff08;Unicaode&#xff09;转回 Char Cha…

杂谈:DC对Verilog和SystemVerilog语言的支持

DC对Verilog和SystemVerilog语言的支持 设计语言用哪种&#xff1f;Design Compiler对二者的支持简单的fsm电路测试测试结果对比写在最后 设计语言用哪种&#xff1f; 直接抛出结论&#xff1a;先有电路&#xff0c;后为描述。设计端而言&#xff0c;没有语言的高低好坏&#…

IMX6ULL板开发——第一个应用程序

实现第一个应用程序&#xff1a;在IMX6ULL开发板上运行程序hello.c #include <stdio.h>/* 执行命令: ./hello weidongshan* argc 2* argv[0] ./hello* argv[1] weidongshan*/int main(int argc, char **argv) {if (argc > 2)printf("Hello, %s!\n", arg…

ASRPRO语音识别模块

ASRPRO语音识别模块 SOFT IIC 与PCA9685模块通信 pca9685 iic通信 地址位 ADDR<<1|0 左移一位 #define I2C_WRITE 0 #define I2C_READ 1 否则通信地址错误 asrpro 通过UART与电脑连接&#xff0c;可以进行简单的交互 将STM32作为接口扩展&#xff0c;通过SPI或I…