xposed微信红包

一、xposed的工作原理

        关于Xposed框架相信大家应该不陌生了,他是Android中Hook技术的一个著名的框架,还有一个框架是CydiaSubstrate,但是这个框架是收费的,而且个人觉得不怎么好用,而Xposed框架是免费的而且还是开源的,网上也有很多文章介绍了Xposed框架的原理实现,不了解的同学可以自行查阅即可,可以参考:http://blog.csdn.net/u012417380/article/details/55254369?locationNum=13&fps=1http://blog.csdn.net/zhangmiaoping23/article/details/54891362,本文主要介绍如何通过这个框架来进行微信自动领取红包。


二、微信自动领红包原理分析

       本文基于微信6.63的版本进行分析,其他微信版本的分析思路类似,不同的微信版本其混淆规则可能不一样,具体可以下载对应的版本进行分析。

       领取微信红包步骤分析

       在领取到微信红包时,我们必须要经历以下几个步骤

       a、打开对应的红包界面(通过状态栏微信红包的提示或者通过聊天界面点击红包)

      

         b、点击上面的按钮“开”领取红包



基于xposed自动领红包思路

我们知道,xposed可以hook住应用程序的方法,假设我们通过xposed的监听知道有红包来了,这个时候就可以直接打开领取微信红包的界面,然后再从界面中找到“开“这个按钮,调用其performClick方法模拟点击,就可以自动领取红包啦。基于以上思路,我们有一个问题需要解决:

a、如何监听有红包来了

b、如何打开领取红包页面

c、如何在领取红包页面找到“开”这个按钮

如果能够解决以上三个问题,那么就可以自动领取红包了,下面我们逐个分析解决上面的问题。


1、监听有红包来了

我们知道,我们在用微信聊天的时候,会将聊天的内容存在手机本地,也就是消息记录,也就是说,一旦有微信接收到消息,会立即将其存入本地的数据库中,而我们知道,微信使用的数据库是WCDB,WCDB 是腾讯出品的一个高效、完整、易用的移动数据库框架,基于SQLCipher,支持iOS, macOS和Android,还内建了Repair Kit用于修复损坏的数据库以及内置的防SQL注入,具体可以参考https://www.jianshu.com/p/ba3eb836bbd7,我们可以用xposed hook住WCDB的插入方法,那么在有消息到来时,就可以插入的数据判断是否是红包消息,如果是则表示有红包来了。


反编译微信apk

要想hook住应用的方法,首先我们需要知道方法的名称,所以第一步,我们需要反编译微信apk

a、下载微信对应版本apk

b、反编译微信apk

方法1:使用jadx反编译(推荐,简单方便)

首先下载jadx,下载地址:https://github.com/skylot/jadx

修改bin\jadx-gui.bat和bin\jadx.bat文件下对"maximum java heap size"的配置,如果不修改,对反编译大一点的apk可能会出现卡死,修改如下:

jadx-gui.bat文件中

@rem Add default JVM options here. You can also use JAVA_OPTS and JADX_GUI_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-d64" "-Xms4g" "-Xmx8g"

jadx.bat文件中

@rem Add default JVM options here. You can also use JAVA_OPTS and JADX_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xms4g" "-Xmx8g"

然后就可以直接在cmd中运行jadx-gui.bat了,会打开jadx的图像界面,然后打开对应的apk即可完成反编译。


方法2:使用dex2jar+jd-gui(方法复杂并且有些内容无法看到)

首先用apktool反编译apk,如下:


反编译之后的目录如下:


可以看到,反编译之后生成的都是smali代码,而我对smali代码不熟悉,所以用dex2jar将应用中的dex转换成jar包,然后用jd-gui打开jar阅读代码,下面我们用dex2jar生成jar包。

将微信apk后缀名改为zip并解压


可以看到,微信采用了分包技术,而用dex2jar并不能将所有的dex合并成一个jar包,所以这里我们采用了dex2jar的改进版本,下载地址如下:https://github.com/pxb1988/dex2jar/releases/download/2.1-nightly-26/dex-tools-2.1-20150601.060031-26.zip

输入命令:


报错了,提示的是OutOfMemoryError,网上查一下解决办法,说是给java分配的内存不够,打开d2j-dex2jar.bat

@"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.Dex2jarCmd %*
它其实是调用执行d2j_invoke.bat这个文件,打开 d2j_invoke.bat

@echo off
REM better invocation scripts for windows from lanchon, release in public domain. thanks!
REM https://code.google.com/p/dex2jar/issues/detail?id=192

setlocal enabledelayedexpansion

set LIB=%~dp0lib

set CP=
for %%X in ("%LIB%"\*.jar) do (
    set CP=!CP!%%X;
)

java -Xms512m -Xmx1024m -cp "%CP%" %*

可以看到,最下面设置了最大内存,修改为

java -Xms1024m -Xmx3072m -cp "%CP%" %*
保存之后再转换jar包,转换成功,如下


用jd-gui打开classes.jar,如下:


c、找到WCDB的插入方法,WCDB的基本架构和SQLITE一样,可以通过SQLiteDatabase类的以下方法插入数据

private int executeSql(String paramString, Object[] paramArrayOfObject, CancellationSignal paramCancellationSignal)
public final long insert(String paramString1, String paramString2, ContentValues paramContentValues)
public final long insertOrThrow(String paramString1, String paramString2, ContentValues paramContentValues)
public final long insertWithOnConflict(String paramString1, String paramString2, ContentValues paramContentValues, int paramInt)
而insert和insertOrThrow最终都会调用insertWithOnConflict,而具体是调用的哪个方法来插入数据的呢?这个我们就需要用xposed hook 以上方法,在方法调用结束后打印日志,然后再给手机发送一个红包,观察xposed打印的日志内容来确定调用了哪个方法,以

insertWithOnConflict方法为例:

  findAndHookMethod("com.tencent.wcdb.database.SQLiteDatabase", lpparam.classLoader, "insertWithOnConflict", String.class, String.class, ContentValues.class, int.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("------------------------insertWithOnConflict called---------------------" + "\n");
                }
            });

接受到红包后,发现日志中有
------------------------insertWithOnConflict called---------------------
则表明是调用该方法插入红包数据到数据库的,将insertWithOnConfict方法的参数全部打印出来,观察红包数据:

  findAndHookMethod("com.tencent.wcdb.database.SQLiteDatabase", lpparam.classLoader, "insertWithOnConflict", String.class, String.class, ContentValues.class, int.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("------------------------insert start---------------------" + "\n\n");
                    log("param args1:" + (String)param.args[0]);
                    log("param args1:" + (String)param.args[1]);
                    ContentValues contentValues = (ContentValues) param.args[2];
                    log("param args3 contentValues:");
                    for (Map.Entry<String, Object> item : contentValues.valueSet())
                    {
                        if (item.getValue() != null) {
                            log(item.getKey() + "---------" + item.getValue().toString());
                        } else {
                            log(item.getKey() + "---------" + "null");
                        }
                    }

                    log("------------------------insert over---------------------" + "\n\n");
            });
接受一个红包,打印数据如下:

02-27 10:32:37.002 I/Xposed  ( 3621): ------------------------insert start---------------------
02-27 10:32:37.002 I/Xposed  ( 3621): 
02-27 10:32:37.002 I/Xposed  ( 3621): param args1:message
02-27 10:32:37.002 I/Xposed  ( 3621): param args1:msgId
02-27 10:32:37.002 I/Xposed  ( 3621): param args3 contentValues:
02-27 10:32:37.002 I/Xposed  ( 3621): reserved---------~SEMI_XML~��.msg.appmsg.wcpayinfo.url��įhttps://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201802276007459178015&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90��.msg.appmsg.wcpayinfo.sceneid��1002��.msg.appmsg.$sdkver������ .msg.appmsg.wcpayinfo.templateid�� 7a2a165d31da7fce6dd77e05c300028a��.msg.fromusername��wxid_perxytujgt5922��.msg.appmsg.des��我给你发了一个红包,赶紧去拆!��.msg.appmsg.wcpayinfo.nativeurl��Ľwxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201802276007459178015&sendusername=wxid_perxytujgt5922&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90��.msg.appmsg.wcpayinfo.iconurl��(https://wx.gtimg.com/hongbao/1800/hb.png��.msg.appmsg.url��įhttps://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201802276007459178015&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90��!.msg.appmsg.wcpayinfo.sendertitle��	恭喜发财,大吉大利��.msg.appmsg.wcpayinfo.senderdes��查看红包��#.msg.appmsg.wcpayinfo.receivertitle��	恭喜发财,大吉大利��.msg.appmsg.wcpayinfo.scenetext��微信红包��.msg.appmsg.title��微信红包��.msg.appmsg.wcpayinfo.paymsgid��1000039501201802276007459178015��.msg.appmsg��
02-27 10:32:37.002 I/Xposed  ( 3621): 	��#.msg.appmsg.wcpayinfo.locallogoicon��c2c_hongbao_icon_cn��.msg.appmsg.thumburl��(https://wx.gtimg.com/hongbao/1800/hb.png��.msg��
02-27 10:32:37.002 I/Xposed  ( 3621): ��!.msg.appmsg.wcpayinfo.receiverdes��领取红包��.msg.appmsg.$appid������.msg.appmsg.type��2001��.msg.appmsg.wcpayinfo��
02-27 10:32:37.002 I/Xposed  ( 3621): 		��.msg.appmsg.wcpayinfo.innertype��0��!.msg.appmsg.wcpayinfo.invalidtime��
02-27 10:32:37.002 I/Xposed  ( 3621): 1519785153�� .msg.appmsg.wcpayinfo.scenetext1��微信红包
02-27 10:32:37.002 I/Xposed  ( 3621): msgId---------133257
02-27 10:32:37.003 I/Xposed  ( 3621): msgSvrId---------9062586593350237923
02-27 10:32:37.003 I/Xposed  ( 3621): talker---------wxid_perxytujgt5922
02-27 10:32:37.003 I/Xposed  ( 3621): content---------<msg>
02-27 10:32:37.003 I/Xposed  ( 3621): 	<appmsg appid="" sdkver="">
02-27 10:32:37.003 I/Xposed  ( 3621): 		<des><![CDATA[我给你发了一个红包,赶紧去拆!]]></des>
02-27 10:32:37.003 I/Xposed  ( 3621): 		<url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201802276007459178015&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90]]></url>
02-27 10:32:37.003 I/Xposed  ( 3621): 		<type><![CDATA[2001]]></type>
02-27 10:32:37.003 I/Xposed  ( 3621): 		<title><![CDATA[微信红包]]></title>
02-27 10:32:37.003 I/Xposed  ( 3621): 		<thumburl><![CDATA[https://wx.gtimg.com/hongbao/1800/hb.png]]></thumburl>
02-27 10:32:37.003 I/Xposed  ( 3621): 		<wcpayinfo>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<templateid><![CDATA[7a2a165d31da7fce6dd77e05c300028a]]></templateid>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201802276007459178015&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90]]></url>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<iconurl><![CDATA[https://wx.gtimg.com/hongbao/1800/hb.png]]></iconurl>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<receivertitle><![CDATA[恭喜发财,大吉大利]]></receivertitle>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<sendertitle><![CDATA[恭喜发财,大吉大利]]></sendertitle>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<scenetext><![CDATA[微信红包]]></scenetext>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<senderdes><![CDATA[查看红包]]></senderdes>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<receiverdes><![CDATA[领取红包]]></receiverdes>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<nativeurl><![CDATA[wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201802276007459178015&sendusername=wxid_perxytujgt5922&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90]]></nativeurl>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<sceneid><![CDATA[1002]]></sceneid>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<innertype><![CDATA[0]]></innertype>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<paymsgid><![CDATA[1000039501201802276007459178015]]></paymsgid>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<scenetext>微信红包</scenetext>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<locallogoicon><![CDATA[c2c_hongbao_icon_cn]]></locallogoicon>
02-27 10:32:37.003 I/Xposed  ( 3621): 			<invalidtime><![CDATA[1519785153]]></invalidtime>
02-27 10:32:37.003 I/Xposed  ( 3621): 		</wcpayinfo>
02-27 10:32:37.003 I/Xposed  ( 3621): 	</appmsg>
02-27 10:32:37.003 I/Xposed  ( 3621): 	<fromusername><![CDATA[wxid_perxytujgt5922]]></fromusername>
02-27 10:32:37.003 I/Xposed  ( 3621): </msg>
02-27 10:32:37.003 I/Xposed  ( 3621): flag---------0
02-27 10:32:37.003 I/Xposed  ( 3621): status---------3
02-27 10:32:37.003 I/Xposed  ( 3621): msgSeq---------709232819
02-27 10:32:37.003 I/Xposed  ( 3621): imgPath---------THUMBNAIL_DIRPATH://th_44a88a61f9d4fcd328322568fe1d597a
02-27 10:32:37.004 I/Xposed  ( 3621): createTime---------1519698753000
02-27 10:32:37.004 I/Xposed  ( 3621): lvbuffer---------[B@571a97
02-27 10:32:37.004 I/Xposed  ( 3621): isSend---------0
02-27 10:32:37.004 I/Xposed  ( 3621): type---------436207665
02-27 10:32:37.004 I/Xposed  ( 3621): bizChatId----------1
02-27 10:32:37.004 I/Xposed  ( 3621): talkerId---------2697
02-27 10:32:37.004 I/Xposed  ( 3621): ------------------------insert over---------------------
从上面的数据中我们可以知道很多红包的信息,比如发送人taker,消息的类型type等,如何确认type对应的就是消息类型呢?这个只能发送多个红包,发现type的值一直都是436207665,而如果是其他的消息,type的值会改变。

现在,我们可以通过判断type的值是否等于436207665来判断是否是微信红包的消息。


2、打开领取红包页面

现在我们可以监听到红包消息了,接下来就是如何打开领取红包页面,首先,打开点开一个红包页面,用hierarchy view查看红包页面对应的activity,如下:


可以看到,领取红包对应的页面为com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI,全局搜索LuckyMoneyReceiveUI,可以看到以下代码

    paramau = new Intent();              
    paramau.putExtra("key_way", i);
    paramau.putExtra("key_native_url", paramView.hcH);
    paramau.putExtra("key_username", parama.crz());
    com.tencent.mm.bm.d.b(parama.getContext(), "luckymoney", ".ui.LuckyMoneyReceiveUI", paramau);

猜测应该是调用com.tencent.mm.bm.d.b方法去启动领取红包页面,找到 com.tencent.mm.bm.d.b,代码如下:

  public static void b(Context paramContext, String paramString1, String paramString2, Intent paramIntent)
  {
    if (!f.fN(21)) {}
    try
    {
      if ((paramContext.getSharedPreferences(ac.cfs(), 0).getBoolean("settings_multi_webview", false)) && (".ui.tools.WebViewUI".endsWith(paramString2)))
      {
        x.i("MicroMsg.PluginHelper", "start multi webview!!!!!!!!!");
        paramIntent.addFlags(134217728);
        paramIntent.addFlags(524288);
      }
      a(paramContext, paramString1, paramString2, paramIntent, true);
      return;
    }
    catch (Exception localException)
    {
      for (;;)
      {
        x.e("MicroMsg.PluginHelper", "%s", new Object[] { localException.getMessage() });
      }
    }
  }
  

也就是说,我们可以通过调用静态方法com.tencent.mm.bm.d.b来启动领取红包页面,观察传入的几个参数,我们需要确定的是paramContext和paramIntent,paramContext应该是一个Activity,那么用哪个Activity来启动红包页面比较合适呢?首先,这个Activity必须存在,如果Activity都不存在,启动红包页面也无从说起,根据这个标准,只有微信主界面是一直存在的,所有,我们用微信的主界面去启动红包页面,我们可以hook住微信主界面的onCreate方法,然后将activity对象保存在xposed程序中,这样就获得了微信主界面的Activity对象。paramIntent这个参数我们等下说,先看如何获得微信主界面对象。


首先,我们打开微信主界面,用hierarchy view查看主界面,如下:


我们再xposed中hook住com.tencent.mm.ui.LauncherUI的onCreate方法,并保存Activity对象

            
            private static Activity launcherUiActivity = null;
            findAndHookMethod("com.tencent.mm.ui.LauncherUI", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("com.tencent.mm.ui.LauncherUI onCreated" + "\n");
                    launcherUiActivity = (Activity) param.thisObject;
                }
            });

现在,我们就可以用launcherUiActivity作为启动领取红包界面的context了。

接下来,就是要确定启动红包界面的Intent,可以看到,调用com.tencent.mm.bm.d.b启动红包页面,Intent需要传入三个参数:

    paramau = new Intent();              
    paramau.putExtra("key_way", i);
    paramau.putExtra("key_native_url", paramView.hcH);
    paramau.putExtra("key_username", parama.crz());
    com.tencent.mm.bm.d.b(parama.getContext(), "luckymoney", ".ui.LuckyMoneyReceiveUI", paramau);

那么如何确定上面三个参数的值呢?可以换一种思路,我们可以hook住红包页面的onCreate方法,然后在onCreate中获得红包页面的Intent,从Intent中取出上面三个参数并打印出来:

            findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    Activity activity = (Activity) param.thisObject;
                    String key_native_url = activity.getIntent().getStringExtra("key_native_url");
                    String key_username = activity.getIntent().getStringExtra("key_username");
                    int key_way = activity.getIntent().getIntExtra("key_way", 0);
                    log("key_native_url: " + key_native_url + "\n");
                    log("key_way: " + key_way + "\n");
                    log("key_username: " + key_username + "\n");
                }
            });

打印出来的结果如下:

02-27 10:32:41.841 I/Xposed  ( 3621): key_native_url: wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201802276007459178015&sendusername=wxid_perxytujgt5922&ver=6&sign=714c8c5371cbdae43fc88d08103c68350cc0fae562fe566d2f3ed15acea68b2ad7f990466ccb157f04f0d97d38b123582019220dce99d2783f72bc3dd653e6e7ea7bced69e82d45d4bcac0db5cc0cf90
02-27 10:32:41.841 I/Xposed  ( 3621): key_way: 1
02-27 10:32:41.841 I/Xposed  ( 3621): key_username: wxid_perxytujgt5922

经过对比发现,key_native_url就是插入数据库中contentValues.content.msg.appmsg.wcpayinfo.nativeurl的值,也就是说,我们可以从插入数据库的红包数据中获得key_native_url;类似的,key_username对应的是contentValues.talker,而key_way经过测试,它的值一直是1,启动红包页面代码如下:

findAndHookMethod("com.tencent.wcdb.database.SQLiteDatabase", lpparam.classLoader, "insertWithOnConflict", String.class, String.class, ContentValues.class, int.class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        // 打印插入数据信息
        log("------------------------insert start---------------------" + "\n\n");
        log("param args1:" + param.args[0]);
        log("param args1:" + param.args[1]);
        ContentValues contentValues = (ContentValues) param.args[2];
        log("param args3 contentValues:");
        for (Map.Entry<String, Object> item : contentValues.valueSet())
        {
            if (item.getValue() != null) {
                log(item.getKey() + "---------" + item.getValue().toString());
            } else {
                log(item.getKey() + "---------" + "null");
            }
        }
        log("------------------------insert over---------------------" + "\n\n");

        // 判断插入的数据是否是发送过来的消息
        String tableName = (String) param.args[0];
        if (TextUtils.isEmpty(tableName) || !tableName.equals("message")) {
            return;
        }
        // 判断是否是红包消息类型
        Integer type = contentValues.getAsInteger("type");
        if (null == type) {
            return;
        }
        if (type == 436207665) {
            // 处理红包消息
            handleLuckyMoney(contentValues, lpparam);
        }
    }
});
// 处理红包消息
private void handleLuckyMoney(ContentValues contentValues, LoadPackageParam lpparam) throws Exception {
    // 获得发送人
    String talker = contentValues.getAsString("talker");

    // 从插入的数据库中获得nativeurl
    String content = contentValues.getAsString("content");
    if (!content.startsWith("<msg")) {
        content = content.substring(content.indexOf("<msg"));
    }

    JSONObject wcpayinfo = new XmlToJson.Builder(content).build()
            .getJSONObject("msg").getJSONObject("appmsg").getJSONObject("wcpayinfo");

    String nativeUrlString = wcpayinfo.getString("nativeurl");
    log("nativeurl: " + nativeUrlString + "\n");

    // 启动红包页面
    if (launcherUiActivity != null) {
        log("call method com.tencent.mm.bm.d b, start LuckyMoneyReceiveUI" + "\n");
        Intent paramau = new Intent();
        paramau.putExtra("key_way", 1);
        paramau.putExtra("key_native_url", nativeUrlString);
        paramau.putExtra("key_username", talker);
        callStaticMethod(findClass("com.tencent.mm.bm.d", lpparam.classLoader), "b", launcherUiActivity, "luckymoney", ".ui.LuckyMoneyReceiveUI", paramau);
    } else {
        log("launcherUiActivity == null" + "\n");
    }
}


3、自动点击红包界面的“开”领取红包

这一步比较简单,我们只需要从红包界面找到“开”按钮,并调用其performClick方法即可,打开hierarchy view,查看“开”按钮的id,如下:

可以看到,“开”按钮的id为"c4j",在反编译的代码的public.xml文件中查找"c4j"

    <public type="id" name="c4j" id="0x7f100f31" />

将id="0x7f100f31”转换为十进制数据,为2131758897,全局搜索这个值,在一个a.java文件中找到该值的定义,如下:

public final class a
	public static final class f {
		public static final int upt = 2131758897;
	}
}

即a.f.upt即为“开”按钮的id引用,全局搜索a.f.upt,在红包页面LuckyMoneyReceiveUI的initView方法中有

    this.ogX = ((Button)findViewById(a.f.upt));

而在LuckyMoneyReceiveUI有定义字段“ogx”

  Button ogX;

到此,我们可以断定LuckyMoneyReceiveUI中的ogx字段即为“开”按钮,接下来我们需要找到给该按钮绑定监听的方法,因为只有绑定监听了之后,我们点击按钮才能出发点击事件,在LuckyMoneyReceiveUI搜索"ogX.setOnClickListener"

  public final boolean d(int paramInt1, int paramInt2, String paramString, final k paramk)
  {
		this.ogX.setOnClickListener(new View.OnClickListener()
		{
		  public final void onClick(View paramAnonymousView)
		  {
		    com.tencent.mm.plugin.report.service.g.pQN.h(11701, new Object[] { Integer.valueOf(5), Integer.valueOf(LuckyMoneyReceiveUI.sk(LuckyMoneyReceiveUI.d(LuckyMoneyReceiveUI.this).obK)), Integer.valueOf(LuckyMoneyReceiveUI.e(LuckyMoneyReceiveUI.this)), Integer.valueOf(0), Integer.valueOf(2) });
		    paramAnonymousView = LuckyMoneyReceiveUI.this;
		    paramAnonymousView.b(new com.tencent.mm.plugin.luckymoney.b.ac(paramAnonymousView.odI.msgType, paramAnonymousView.odI.fdS, paramAnonymousView.odI.nZa, paramAnonymousView.odI.fLC, n.aXg(), q.FU(), paramAnonymousView.getIntent().getStringExtra("key_username"), "v1.0", paramAnonymousView.odI.odE), false);
		    n.b(paramAnonymousView.ogX);
		  }
		});
   }

在方法"d"中找到了给“开”按钮设置监听的方法,现在我们只需要hook住"d"方法,在其调用完毕之后,通过Xposed提供的方法获取到“开”按钮,调用其performClick方法,即可完成自动点击“开”按钮,代码如下:

            // hook红包界面初始化“开”按钮的方法,在该方法完成后自动点击开按钮领取红包
            findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", lpparam.classLoader, "d", int.class, int.class, String.class, findClass("com.tencent.mm.ae.k", lpparam.classLoader), new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI: Method d called" + "\n");
                    Field buttonField = XposedHelpers.findField(param.thisObject.getClass(), "ogX");
                    final Button kaiButton = (Button) buttonField.get(param.thisObject);
                    kaiButton.performClick();
                }
            });
三、完整代码
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import org.json.JSONObject;
import java.lang.reflect.Field;
import java.util.Map;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import me.veryyoung.wechat.luckymoney.util.XmlToJson;

import static de.robv.android.xposed.XposedBridge.log;
import static de.robv.android.xposed.XposedHelpers.callStaticMethod;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findClass;
import static me.veryyoung.wechat.luckymoney.VersionParam.WECHAT_PACKAGE_NAME;


public class Main implements IXposedHookLoadPackage {
    private static Activity launcherUiActivity = null;

    @Override
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (lpparam.packageName.equals(WECHAT_PACKAGE_NAME)) {
            // hook微信插入数据的方法,监听红包消息
            findAndHookMethod("com.tencent.wcdb.database.SQLiteDatabase", lpparam.classLoader, "insertWithOnConflict", String.class, String.class, ContentValues.class, int.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    // 打印插入数据信息
                    log("------------------------insert start---------------------" + "\n\n");
                    log("param args1:" + param.args[0]);
                    log("param args1:" + param.args[1]);
                    ContentValues contentValues = (ContentValues) param.args[2];
                    log("param args3 contentValues:");
                    for (Map.Entry<String, Object> item : contentValues.valueSet())
                    {
                        if (item.getValue() != null) {
                            log(item.getKey() + "---------" + item.getValue().toString());
                        } else {
                            log(item.getKey() + "---------" + "null");
                        }
                    }
                    log("------------------------insert over---------------------" + "\n\n");

                    // 判断插入的数据是否是发送过来的消息
                    String tableName = (String) param.args[0];
                    if (TextUtils.isEmpty(tableName) || !tableName.equals("message")) {
                        return;
                    }
                    // 判断是否是红包消息类型
                    Integer type = contentValues.getAsInteger("type");
                    if (null == type) {
                        return;
                    }
                    if (type == 436207665) {
                        // 处理红包消息
                        handleLuckyMoney(contentValues, lpparam);
                    }
                }
            });

            // hook 微信主界面的onCreate方法,获得主界面对象
            findAndHookMethod("com.tencent.mm.ui.LauncherUI", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("com.tencent.mm.ui.LauncherUI onCreated" + "\n");
                    launcherUiActivity = (Activity) param.thisObject;
                }
            });

            // hook领取红包页面的onCreate方法,打印Intent中的参数(只起到调试作用)
            findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    Activity activity = (Activity) param.thisObject;
                    String key_native_url = activity.getIntent().getStringExtra("key_native_url");
                    String key_username = activity.getIntent().getStringExtra("key_username");
                    int key_way = activity.getIntent().getIntExtra("key_way", 0);
                    log("key_native_url: " + key_native_url + "\n");
                    log("key_way: " + key_way + "\n");
                    log("key_username: " + key_username + "\n");
                }
            });

            // hook红包界面初始化“开”按钮的方法,在该方法完成后自动点击开按钮领取红包
            findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", lpparam.classLoader, "d", int.class, int.class, String.class, findClass("com.tencent.mm.ae.k", lpparam.classLoader), new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    log("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI: Method d called" + "\n");
                    Field buttonField = XposedHelpers.findField(param.thisObject.getClass(), "ogX");
                    final Button kaiButton = (Button) buttonField.get(param.thisObject);
                    kaiButton.performClick();
                }
            });
        }

    }

    // 处理红包消息
    private void handleLuckyMoney(ContentValues contentValues, LoadPackageParam lpparam) throws Exception {
        // 获得发送人
        String talker = contentValues.getAsString("talker");

        // 从插入的数据库中获得nativeurl
        String content = contentValues.getAsString("content");
        if (!content.startsWith("<msg")) {
            content = content.substring(content.indexOf("<msg"));
        }

        JSONObject wcpayinfo = new XmlToJson.Builder(content).build()
                .getJSONObject("msg").getJSONObject("appmsg").getJSONObject("wcpayinfo");

        String nativeUrlString = wcpayinfo.getString("nativeurl");
        log("nativeurl: " + nativeUrlString + "\n");

        // 启动红包页面
        if (launcherUiActivity != null) {
            log("call method com.tencent.mm.bm.d b, start LuckyMoneyReceiveUI" + "\n");
            Intent paramau = new Intent();
            paramau.putExtra("key_way", 1);
            paramau.putExtra("key_native_url", nativeUrlString);
            paramau.putExtra("key_username", talker);
            callStaticMethod(findClass("com.tencent.mm.bm.d", lpparam.classLoader), "b", launcherUiActivity, "luckymoney", ".ui.LuckyMoneyReceiveUI", paramau);
        } else {
            log("launcherUiActivity == null" + "\n");
        }
    }
}

四、总结

1、不要急着解决问题,首先要分析问题,找到关键的切入点;

2、如果一个问题正面不好解决,可以根据结果去推测解决问题的方法,反复尝试。


严重声明

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














  • 11
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值