Xposed系列之微信裝X指南(二)

需求

比如說每個(gè)人都有一個(gè)小目標(biāo)桨吊,每天看看自己的目標(biāo)會(huì)更有動(dòng)力,那么現(xiàn)在實(shí)現(xiàn)"一個(gè)億"的小目標(biāo),我們讓從點(diǎn)擊我的Tab開始服傍,
進(jìn)入支付頁面-錢包頁面-零錢頁面顯示的金額都是¥100000000.00

結(jié)果

微信Android7.0.16版本卷雕,最后代碼實(shí)現(xiàn)的效果如下:

image

舊版本實(shí)現(xiàn)效果

準(zhǔn)備工作

Root相關(guān)

  • TWRP Recovery:一款強(qiáng)大的第三方recovery节猿,有著官方Recovery無法做到的功能,安裝Magisk Manager需要用到
  • Magisk Manager:一款強(qiáng)大的刷Root工具漫雕,可以替代SuperSU進(jìn)行root權(quán)限管理

如果用VirtualXposed或太極或其他虛擬環(huán)境的Xposed則可以不用Root滨嘱,如果用Xposed Installer則需要Root權(quán)限,
關(guān)于這三者可看上篇文章:Xposed系列之Demo上手指南及源碼解析(一)

反編譯相關(guān)

  • apktool:主要用于反編譯APK查看資源文件
  • dex2jar:反編譯APK得到Java源代碼Jar包
  • jd-gui:一般配合dex2jar使用浸间,將其反編譯得到的jar包拖進(jìn)gui方便查看源碼
  • jadx:功能強(qiáng)大的反編譯工具太雨,可以直接查看Java代碼和資源文件等,同時(shí)支持查看Smali

推薦使用jadx工具反編譯魁蒜,如從微信官網(wǎng)下載的32位版本微信囊扳,默認(rèn)都是會(huì)下載上面weixin7016android1700_arm64.apk
如果下載arm64版本則有些x86架構(gòu)的模擬器不支持微信就不能正常使用了兜看,所以用32位主要是為了方便在模擬器上調(diào)試

image

然后將下載下來的weixin7016android1700.apk拖入JadxGUI中锥咸,反編譯結(jié)果如下圖:
image

并且我們還可以另存為Gradle項(xiàng)目,在Android Studio或IDEA中查看铣减,操作入口如下:
image

Hook分析相關(guān)

adb命令

adb 命令可以參考https://github.com/xbdcc/CCommand她君, 這里簡單介紹這里需要使用的:

adb shell dumpsys activity top > activity_top.txt

當(dāng)然如果adb命令你已經(jīng)很熟悉了想快捷輸入也可以在環(huán)境變量中設(shè)置alisa

  • 控制臺(tái)中輸入vim ~/.bash_profile
  • 在其中加入一行設(shè)置adb shell dumpsys activity top > activity_top.txt的別名為activity_top葫哗,如下:
alias activity_top="adb shell dumpsys activity top > activity_top.txt"
  • 然后按esc鍵缔刹,再輸入:wq回車退出并保存
  • 再輸入source ~/.bash_profile使環(huán)境變量生效
  • 最后控制臺(tái)直接輸入activity_top回車就和剛剛一長串命令一樣的效果了

該命令可以輸出當(dāng)前Activity信息球涛,如命令抓取的零錢頁面Activity的信息如下:

TASK com.tencent.mm id=184
  ACTIVITY com.tencent.mm/.plugin.wallet.balance.ui.WalletBalanceManagerUI 1fcb6708 pid=1326
    Local Activity 27bbdf64 State:
      mResumed=true mStopped=false mFinished=false
      mLoadersStarted=true
      mChangingConfigurations=false
      mCurrentConfig={1.0 ?mcc?mnc zh_CN ?layoutDir sw360dp w360dp h622dp 480dpi nrml long port finger -keyb/v/h -nav/h s.5mThemeChanged = 0mThemeChangedFlags = 0mFlipFont = 0}
    Active Fragments in 247455f2:
      #0: ReportFragment{22e48443 #0 android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag}
        mFragmentId=#0 mContainerId=#0 mTag=android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag
        mState=5 mIndex=0 mWho=android:fragment:0 mBackStackNesting=0
        mAdded=true mRemoving=false mResumed=true mFromLayout=false mInLayout=false
        mHidden=false mDetached=false mMenuVisible=true mHasMenu=false
        mRetainInstance=false mRetaining=false mUserVisibleHint=true
        mFragmentManager=FragmentManager{247455f2 in WalletBalanceManagerUI{27bbdf64}}
        mActivity=com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI@27bbdf64
        Child FragmentManager{a6aeac0 in ReportFragment{22e48443}}:
          FragmentManager misc state:
            mActivity=com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI@27bbdf64
            mContainer=android.app.Fragment$1@138ca3f9
            mParent=ReportFragment{22e48443 #0 android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag}
            mCurState=5 mStateSaved=false mDestroyed=false
    Added Fragments:
      #0: ReportFragment{22e48443 #0 android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag}
    FragmentManager misc state:
      mActivity=com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI@27bbdf64
      mContainer=android.app.Activity$1@386ddf3e
      mCurState=5 mStateSaved=false mDestroyed=false
    ViewRoot:
      mAdded=true mRemoved=false
      mConsumeBatchedInputScheduled=false
      mConsumeBatchedInputImmediatelyScheduled=false
      mPendingInputEventCount=0
      mProcessInputEventsScheduled=false
      mTraversalScheduled=false
      android.view.ViewRootImpl$NativePreImeInputStage: mQueueLength=0
      android.view.ViewRootImpl$ImeInputStage: mQueueLength=0
      android.view.ViewRootImpl$NativePostImeInputStage: mQueueLength=0
    Choreographer:
      mFrameScheduled=false
      mLastFrameTime=16770070 (150010 ms ago)
    View Hierarchy:
      com.android.internal.policy.impl.PhoneWindow$DecorView{162d5b8c V.ED.... R....... 0,0-1080,1920}
        com.tencent.mm.ui.widget.SwipeBackLayout{15900669 VFE..... ........ 0,0-1080,1920 #7f09245b app:id/g2s}
          com.tencent.mm.ui.statusbar.b{303be18f V.ED.... ........ 0,0-1080,1920}
            android.widget.LinearLayout{1d1aa478 V.E..... ........ 0,54-1080,1920}
              android.view.ViewStub{2f44c751 G.E..... ......I. 0,0-0,0 #1020373}
              android.widget.FrameLayout{ea9bbb6 V.E..... ........ 0,0-1080,1866}
                android.support.v7.widget.ActionBarOverlayLayout{1e0ddd42 V.E..... ........ 0,0-1080,1866 #7f090a81 app:id/b8w}
                  android.support.v7.widget.ContentFrameLayout{6698a90 V.E..... ........ 0,130-1080,1866 #1020002 android:id/content}
                    com.tencent.mm.ui.LayoutListenerView{941b6fc V.E..... ........ 0,0-1080,1736 #7f0917b0 app:id/dp5}
                      android.widget.ScrollView{cc9bba6 VFED.... ........ 0,0-1080,1736}
                        android.widget.RelativeLayout{3887e994 V.E..... ........ 0,0-1080,1736}
                          android.widget.TextView{3781bc3d G.ED.... ......I. 0,0-0,0 #7f09289c app:id/guw}
                          android.widget.LinearLayout{15c40b00 V.E..... ........ 0,0-1080,1736}
                            android.widget.ImageView{1d2df39 V.ED.... ........ 453,130-626,303 #7f09034d app:id/w5}
                            android.widget.TextView{25e2997e V.ED.... ........ 448,389-632,451 #7f09259c app:id/ga5}
                            android.widget.LinearLayout{21b3eedf V.E..... ........ 0,451-1080,721}
                              android.widget.RelativeLayout{bded72c V.E..... ........ 0,43-1080,157}
                                com.tencent.mm.plugin.wallet_core.ui.view.WcPayMoneyLoadingView{b7935f5 V.E..... ........ 375,0-705,114 #7f0928aa app:id/gv_}
                                  com.robinhood.ticker.TickerView{21299fb V.ED.... ........ 0,0-330,114 #7f091794 app:id/dod}
                                android.widget.ProgressBar{1ff83a18 G.ED.... ......I. 0,0-0,0 #7f092905 app:id/gxq}
                              android.widget.LinearLayout{36ce5a56 G.E..... ......I. 0,0-0,0 #7f09033f app:id/vr}
                                android.widget.TextView{29e6dad7 V.ED.... ......ID 0,0-0,0 #7f090340 app:id/vs}
                                android.widget.ImageView{742dfc4 V.ED.... ......ID 0,0-0,0 #7f09033e app:id/vq}
                              android.widget.LinearLayout{d3f2ead G.E..... ......I. 0,0-0,0 #7f09034b app:id/w3}
                                com.tencent.mm.pluginsdk.ui.applet.CdnImageView{28544d73 V.ED.... ......I. 0,0-0,0 #7f09034a app:id/w2}
                                com.tencent.mm.wallet_core.ui.WalletTextView{35703430 V.ED.... ......ID 0,0-0,0 #7f090349 app:id/w1}
                                com.tencent.mm.pluginsdk.ui.applet.CdnImageView{3dcd08a9 V.ED.... ......I. 0,0-0,0 #7f090348 app:id/w0}
                            android.widget.Space{111c4dcf I.ED.... ......I. 0,721-1080,1101}
                            android.widget.LinearLayout{25dc635c V.E..... ........ 0,1187-1080,1295}
                              android.widget.Button{3a448665 VFED..C. ........ 291,0-788,108 #7f0919c4 app:id/e3i}
                              android.widget.Button{b7a5948 GFED..C. ......I. 0,0-0,0 #7f09289d app:id/gux}
                              android.widget.LinearLayout{363e27c7 G.E..... ......I. 0,0-0,0 #7f09141b app:id/d1c}
                                android.widget.TextView{137fc1f4 V.ED.... ......ID 0,0-0,0 #7f09141c app:id/d1d}
                                android.widget.ImageView{31361d1d G.ED.... ......I. 0,0-0,0 #7f09153c app:id/d96}
                            android.widget.LinearLayout{12576b92 V.E..... ........ 464,1554-616,1606}
                              android.widget.TextView{36eb7963 V.ED..C. ........ 0,0-152,52 #7f09289f app:id/guz}
                              android.view.View{181c6e19 G.ED.... ......I. 0,0-0,0 #7f09289e app:id/guy}
                              android.widget.TextView{2dfcaede G.ED.... ......I. 0,0-0,0 #7f09289b app:id/guv}
                            android.widget.TextView{2f9248bf V.ED.... ........ 0,1628-1080,1671 #7f092951 app:id/gzs}
                      android.widget.Button{39a3e185 GFED..C. ......ID 0,0-0,0 #7f0917c6 app:id/dpq}
                  android.support.v7.widget.ActionBarContainer{1e880f89 V.ED.... ........ 0,0-1080,130 #7f090059 app:id/bp}
                    android.support.v7.widget.Toolbar{1a08b8e V.E..... ........ 0,0-1080,130 #7f090057 app:id/bn}
                      android.widget.LinearLayout{88b4a54 V.E..... ........ 0,0-810,130 #7f09005b app:id/br}
                        android.widget.LinearLayout{24ec6dfd V.E...C. ........ 0,0-108,130 #7f0900a0 app:id/dm}
                          com.tencent.mm.ui.widget.imageview.WeImageView{10dc1af2 V.ED.... ........ 22,0-86,130 #7f0900a1 app:id/dn}
                        android.widget.LinearLayout{1e96ecf9 G.E..... ......I. 0,0-0,0 #7f090098 app:id/de}
                          com.tencent.mm.ui.widget.AlbumChooserView{40cf43e V.E...C. ......I. 0,0-0,0 #7f09008a app:id/d1}
                            android.widget.RelativeLayout{13558fec V.E..... ......I. 0,0-0,0}
                              android.widget.TextView{9d05fb5 V.ED.... ......ID 0,0-0,0 #7f090120 app:id/h3}
                              android.widget.FrameLayout{25e0164a V.E..... ......I. 0,0-0,0}
                                com.tencent.mm.ui.widget.imageview.WeImageView{2e7039bb V.ED.... ......ID 0,0-0,0 #7f09011f app:id/h2}
                        android.widget.LinearLayout{1901bed8 V.E..... ......I. 108,0-108,130 #7f0925d1 app:id/gbk}
                          android.widget.LinearLayout{20f44231 V.E..... ......ID 0,0-0,130}
                            android.widget.ImageView{17114d16 G.ED.... ......I. 0,0-0,0 #7f0925d0 app:id/gbj}
                            android.widget.TextView{cdeb697 V.ED.... ......ID 0,34-0,96 #1020014 android:id/text1}
                            android.widget.ProgressBar{263ff084 G.ED.... ......I. 0,0-0,0 #7f091c5e app:id/eki}
                          android.widget.TextView{364d24a2 G.ED.... ......I. 0,0-0,0 #1020015 android:id/text2}
                      android.support.v7.widget.ActionMenuView{c9308ac V.E..... ........ 810,0-1080,130}
                        android.widget.LinearLayout{25f3ae17 V.E..... ........ 0,0-270,130}
                          android.widget.ImageButton{24965204 GFED..C. ......I. 0,0-0,0 #7f09007c app:id/cn}
                          android.widget.TextView{141653ed V.ED..CL ........ 0,0-270,130 #7f090079 app:id/ck}
                          android.widget.LinearLayout{357b14b3 G.E..... ......I. 0,0-0,0 #7f090158 app:id/il}
                            android.widget.ImageView{3b288a70 V.ED.... ......ID 0,0-0,0}
                          android.widget.Button{d1141e9 GFED..CL ......I. 0,0-0,0 #7f090076 app:id/ch}
                          android.widget.RelativeLayout{1908490f V.E..... ......ID 270,65-270,65}
                            com.tencent.mm.ui.widget.imageview.WeImageView{3f63dd9c G.ED.... ......I. 0,0-0,0 #7f090078 app:id/cj}
                            android.widget.ImageView{2db313a5 G.ED.... ......I. 0,0-0,0 #7f090b6f app:id/beb}
                    android.support.v7.widget.ActionBarContextView{18591b45 G.E..... ......I. 0,0-0,0 #7f090065 app:id/c1}
    Looper (main, tid 1) {1f1b79df}
      Message 0: { when=+6m42s348ms what=26 target=com.tencent.mm.sdk.platformtools.ao$2 }
      Message 1: { when=+26m42s267ms what=23 target=com.tencent.mm.sdk.platformtools.ao$2 }
      (Total messages: 2, idling=false, quitting=false)
    Local FragmentActivity 27bbdf64 State:
      mCreated=true mResumed=true mStopped=false    FragmentManager misc state:
      mHost=android.support.v4.app.FragmentActivity$a@1273f2ec
      mContainer=android.support.v4.app.FragmentActivity$a@1273f2ec
      mCurState=4 mStateSaved=false mStopped=false mDestroyed=false

monitor

  • image
    可以查看布局元素和trace信息,分析布局可以查看之前文章Android通過輔助功能實(shí)現(xiàn)搶微信紅包原理簡單介紹
  • image

    校镐,分析trace方法調(diào)用棧亿扁,例如下:


    image
  • image

    ,生成生成html格式的trace鸟廓,可以分析卡頓丟幀等問題从祝,如果有打Trace也可以在上面看出來∫眨可以在Chrome里打開查看牍陌,如下:


    image

再介紹三種方便查找id值的方法

假如我們要查看id為dod的值:

通過activity_top查看

在里面找到元素對(duì)應(yīng)的值,為十六進(jìn)制值员咽。如上面結(jié)果中有這樣一行毒涧,可以看到id為'dod'的值為十六進(jìn)制值7f091794

com.robinhood.ticker.TickerView{21299fb V.ED.... ........ 0,0-330,114 #7f091794 app:id/dod}

通過apk查看

可以把a(bǔ)pk拖進(jìn)AS中,在resources.arsc下選擇你要找的id贝室,得到的值為十六進(jìn)制

image

通過jadx查看

雙擊resources.arsc契讲,可以在里面搜索id,得到的值為十進(jìn)制滑频。如id為dod的值為十進(jìn)制值2131302292

image

Hook分析

通過activity_top得知:

  • 支付頁面:com.tencent.mm/.plugin.mall.ui.MallIndexUI
  • 錢包頁面:com.tencent.mm/.plugin.mall.ui.MallWalletUI
  • 零錢頁面:com.tencent.mm/.plugin.wallet.balance.ui.WalletBalanceManagerUI

分析布局

  • 首先打開支付頁面我們通過monitor的Dump View Hierarchy捡偏,得知顯示錢包的金額的控件id為dod
    支付頁面View視圖
  • 然后我們通過activity_top找到這個(gè)id的地方知道它其實(shí)是com.robinhood.ticker.TickerView這個(gè)控件,
    嗯這個(gè)一看就是不是騰訊的自定義View而是用的第三方庫峡迷,Github上一搜银伟,可以知道它用的是ticker這個(gè)庫,
    后面可以看到錢包頁面和零錢頁面顯示金額的也是用的這個(gè)控件凉当。
    知道了這個(gè)庫我們可以看下TickerView這個(gè)類的代碼枣申,這里再推薦一個(gè)Chrome插件octotree比較方便在GitHub網(wǎng)頁上切換文件,
    如下看杭,可以看到這里有個(gè)setText方法,里面執(zhí)行了columnManager.setText(targetText);代碼挟伙,而columnManager最后執(zhí)行了columnManager.draw(canvas, textPaint);把文字繪制到Canvas上了楼雹,
    setContentDescription(text);設(shè)置了contentDescription的值,所以這就是我們能看到描述和顯示金額的值一樣尖阔,但是它的text屬性值卻為空的原因了贮缅。
    image

分析支付頁面MallIndexUI

  • 首先我們觀察到每次進(jìn)入支付頁面它的金額旁邊是有個(gè)loading的,并且從錢包頁面返回到支付頁面也都會(huì)loading一下介却,那么可以猜想它可能是在onResume里面做了什么操作
    (其實(shí)直接看它代碼一下就能看出來谴供,假設(shè)我們還沒看代碼先簡單猜下)。那么我們看下它的onResume方法調(diào)用棧如下:


    image

    看到了嗎齿坷?里面主要就執(zhí)行了MallIndexBaseUI(MallIndexUI的父類)的onResume和自己的dbb方法桂肌,這個(gè)時(shí)候如果你不想看源碼繼續(xù)分析的話其實(shí)就已經(jīng)可以嘗試Hook跑起來看看效果了数焊,
    本著保險(xiǎn)起見我們還是先繼續(xù)看看它的源碼

    public final void dbb() {
        AppMethodBeat.i(66131);
        ac.i("MicorMsg.MallIndexUI", "updateBalanceNum");
        ak akVar = new ak();
        if (akVar.erV()) {
            this.uCo.setText((String) g.agR().agA().get(ah.a.USERINFO_WALLET_RELEAY_NAME_BALANCE_CONTENT_STRING_SYNC, (Object) getString(R.string.eex)));
            this.uCo.setVisibility(0);
            this.uDf.setVisibility(8);
            this.uDg.setVisibility(8);
            AppMethodBeat.o(66131);
            return;
        }
        if (akVar.erX()) {
            ac.i("MicorMsg.MallIndexUI", "show balance amount");
            long longValue = ((Long) ((com.tencent.mm.plugin.wxpay.a.a) g.ad(com.tencent.mm.plugin.wxpay.a.a.class)).getWalletCacheStg().get(ah.a.USERINFO_NEW_BALANCE_LONG_SYNC, (Object) 0L)).longValue();
            if (this.uDf != null) {
                nQ(akVar.erZ());
                if (this.uDf.getVisibility() == 0) {
                    this.uDf.setMoney(com.tencent.mm.wallet_core.ui.e.C(com.tencent.mm.wallet_core.ui.e.a(String.valueOf(longValue), "100", 2, RoundingMode.HALF_UP).doubleValue()));
                    AppMethodBeat.o(66131);
                    return;
                }
            } else {
                ac.w("MicorMsg.MallIndexUI", "moneyLoadingView is null");
            }
        }
        AppMethodBeat.o(66131);
    }
    

    可以看出ac.i應(yīng)該就是打印的log方法,根據(jù)它的日志updateBalanceNum崎场,可以知道這個(gè)方法主要是更新余額的佩耳,那么我們是不是可以手動(dòng)攔截這個(gè)方法替換為自己設(shè)置的呢?我們來試試谭跨,
    拿到方法的對(duì)象轉(zhuǎn)為Activity干厚,然后通過findViewById找到顯示金額的控件,通過反射拿到setText并且調(diào)用賦值螃宙,或者通過XposedHelpers.callMethod(view, "setText", money)

    private fun hookPayPage() {
        XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.mall.ui.MallIndexUI", classLoader, "dbb", object : XC_MethodReplacement() {
            override fun replaceHookedMethod(param: MethodHookParam?): Any {
                param?.let {
                    val activity = param.thisObject as Activity
                    var view = activity.findViewById<View>(0x7f091794) //id:dod的id值為0x7f091794
                    val method = view.javaClass.getDeclaredMethod("setText", String::class.java)
                    method.invoke(view, money)
                    xlog("Hook method dbb of MallIndexUI class and set money.")
                }
                return ""
            }
        })
    }
    
  • 其實(shí)剛剛在dbb方法有這樣一個(gè)方法this.uDf.setMoney蛮瞄,通過方法名知道它是設(shè)置金額的,所以我們也可以從這里下手谆扎,該方法代碼如下:

    public void setMoney(String str) {
        AppMethodBeat.i(71606);
        cc(str, false);
        AppMethodBeat.o(71606);
    }
    

    再來看cc這個(gè)方法挂捅,第一個(gè)if它首先是判斷了如果傳進(jìn)來的strnull則返回,第二個(gè)if在WcPayMoneyLoadingView類里面我們可以看到this.BNi只在setFirstMoney方法里面賦值this.BNi = str;燕酷,
    reset方法里清空該值籍凝,并且進(jìn)到bs類里面可以看到isNullOrNil方法如果this.BNi值為null或者字符串長度小于等于0才返回true,
    所以這里其實(shí)是判斷這個(gè)字段是否為空苗缩,如果不為空就直接setFirstMoney饵蒂,如果為空就setNewMoney,和下面邏輯一致酱讶。

    public final void cc(String str, boolean z) {
        AppMethodBeat.i(71607);
        if (str == null) {
            AppMethodBeat.o(71607);
            return;
        }
        if (bs.isNullOrNil(this.BNi)) {
            setFirstMoney(str);
            if (z) {
                removeCallbacks(this.BNk);
                AppMethodBeat.o(71607);
                return;
            }
        } else {
            setNewMoney(str);
        }
        AppMethodBeat.o(71607);
    }
    

    繼續(xù)看if (z)這個(gè)判斷退盯,里面主要執(zhí)行了removeCallbacks(this.BNk);,而this.BNk是一個(gè)Runnable對(duì)象如下泻肯,
    根據(jù)日志show loading pb知道這里顯示了loading Progress渊迁,設(shè)置了它為可見的,最終其實(shí)可以找到它其實(shí)id為gxq顯示金額控件后面的Progress

    public Runnable BNk = new Runnable() {
        public final void run() {
            AppMethodBeat.i(71596);
            ac.i("MicroMsg.WcPayMoneyLoadingView", "show loading pb");
            WcPayMoneyLoadingView.this.iIW.setVisibility(0);
            boolean unused = WcPayMoneyLoadingView.this.BNj = true;
            AppMethodBeat.o(71596);
        }
    };
    

    所以我們可以Hookcc這個(gè)方法灶挟,在它執(zhí)行前修改第一個(gè)參數(shù)值為你的金額'money`琉朽,如下:

    private fun hookPayPage2() {
        XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.wallet_core.ui.view.WcPayMoneyLoadingView", classLoader, "cc", String::class.java, Boolean::class.java,
            object : XC_MethodHook() {
                override fun beforeHookedMethod(param: MethodHookParam?) {
                    param?.let{
                        val view = param.thisObject as View
                        param.args[0] = money
                    }
                }
            })
    }
    

分析錢包頁面WalletBalanceManagerUI

  • 前面分析支付頁面知道WcPayMoneyLoadingView中有setFirstMoneysetNewMoney,其實(shí)我們就可以直接Hook替換這兩個(gè)方法稚铣,
    而且該方法通用三個(gè)頁面都有效箱叁,如下:

    private fun hookMoney() {
        val hookClass = classLoader.loadClass(wechatMoneyLoadingView) ?: return
        XposedHelpers.findAndHookMethod(hookClass, "setFirstMoney", String::class.java, replaceStr)
        XposedHelpers.findAndHookMethod(hookClass, "setNewMoney", String::class.java, replaceStr)
    }
    
    private val replaceStr = object : XC_MethodHook() {
        override fun beforeHookedMethod(param: MethodHookParam?) {
            param?.let {
                val view = param.thisObject as View
                when(view.context.javaClass.name) {
                    wechatWalletActivity -> param.args[0] = "¥$money"
                    wechatPayActivity, wechatChangeActivity -> param.args[0] = money
                }
            }
        }
    }
    

分析零錢頁面WalletBalanceManagerUI

  • 首先我們看下零錢頁面WalletBalanceManagerUI里面聲明的對(duì)象,再根據(jù)View Hierarchy我們知道AZo是在TickerView外面的WcPayMoneyLoadingView,
    繼續(xù)看this.AZo.cc惕医,可以發(fā)現(xiàn)它調(diào)用的之前支付頁面分析的WcPayMoneyLoadingView中的tk方法耕漱,所以我們可以直接Hook這個(gè)方法,如下:

    private fun hookChangePage() {
        XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI", classLoader, "tk", Boolean::class.java, object : XC_MethodReplacement() {
            override fun replaceHookedMethod(param: MethodHookParam?): Any {
                param?.let {
                    val activity = param.thisObject as Activity
                    var view = activity.findViewById<View>(0x7f091794) //id:dod的id值為0x7f091794
                    XposedHelpers.callMethod(view, "setText", money)
                    xlog("Hook method tk of WalletBalanceManagerUI class and set money.")
                }
                return ""
            }
        })
    }
    

結(jié)語

其實(shí)分析的時(shí)候可能有點(diǎn)復(fù)雜抬伺,并且要善于用多種工具一起分析螟够,但是最后實(shí)現(xiàn)的代碼很簡單,整理后代碼如下:

class WechatHook : IXposedHookLoadPackage {

    private val packageName = "com.tencent.mm"
    private lateinit var classLoader: ClassLoader
    private val wechatPayActivity = "com.tencent.mm.plugin.mall.ui.MallIndexUI"
    private val wechatWalletActivity = "com.tencent.mm.plugin.mall.ui.MallWalletUI"
    private val wechatChangeActivity =
        "com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI"
    private val wechatMoneyLoadingView =
        "com.tencent.mm.plugin.wallet_core.ui.view.WcPayMoneyLoadingView"
    private var money = "100000000.00"

    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
        if (packageName == lpparam.packageName) {
            xlog("Load Wechat app.")
            classLoader = lpparam.classLoader

            hookMoney()

//            hookPayPage()
//            hookPayPage2()
//
//            hookChangePage()

        }
    }

    /**
     * 改變自定義的文本控件設(shè)置文本方法,終極boss妓笙,三個(gè)都可以
     */
    private fun hookMoney() {
        val hookClass = classLoader.loadClass(wechatMoneyLoadingView) ?: return
        XposedHelpers.findAndHookMethod(hookClass, "setFirstMoney", String::class.java, replaceStr)
        XposedHelpers.findAndHookMethod(hookClass, "setNewMoney", String::class.java, replaceStr)
    }

    /**
     * 支付頁面改變文本
     */
    private fun hookPayPage() = XposedHelpers.findAndHookMethod(
        wechatPayActivity,
        classLoader,
        "dbb",
        replaceViewText
    )

    /**
     * 支付頁面改變文本另一種方法
     */
    private fun hookPayPage2() {
        XposedHelpers.findAndHookMethod(wechatMoneyLoadingView,
            classLoader,
            "cc",
            String::class.java,
            Boolean::class.java,
            object : XC_MethodHook() {
                override fun beforeHookedMethod(param: MethodHookParam?) {
                    param?.let {
                        val view = param.thisObject as View
                        param.args[0] = money
                    }
                }
            })
    }

    /**
     * 零錢頁面改變文本
     */
    private fun hookChangePage() = XposedHelpers.findAndHookMethod(
        wechatChangeActivity,
        classLoader,
        "tk",
        Boolean::class.java,
        replaceViewText
    )

    /**
     * 在方法調(diào)用前手動(dòng)修改值來改變最后顯示的金額
     */
    private val replaceStr = object : XC_MethodHook() {
        override fun beforeHookedMethod(param: MethodHookParam?) {
            param?.let {
                xlog("")
                val view = param.thisObject as View
                when (view.context.javaClass.name) {
                    wechatWalletActivity -> param.args[0] = "¥$money"
                    wechatPayActivity, wechatChangeActivity -> param.args[0] = money
                }
            }
        }
    }

    /**
     * 通過找到顯示金額的控件反射拿到它的賦值方法并調(diào)用
     */
    private val replaceViewText = object : XC_MethodReplacement() {
        override fun replaceHookedMethod(param: MethodHookParam?): Any {
            param?.let {
                val activity = param.thisObject as Activity
                var view = activity.findViewById<View>(0x7f091794) //id:dod的id值為0x7f091794
                XposedHelpers.callMethod(view, "setText", money)
                xlog("find view and set text.")
            }
            return ""
        }
    }
}

建了個(gè)Android逆向Xposed無障礙技術(shù)學(xué)習(xí)QQ交流群若河,有興趣可加979419864,為篩選非技術(shù)愛好者設(shè)置了驗(yàn)證問題輸入0x11的二進(jìn)制結(jié)果给郊,有興趣可加入一起學(xué)習(xí)

參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牡肉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子淆九,更是在濱河造成了極大的恐慌统锤,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炭庙,死亡現(xiàn)場(chǎng)離奇詭異饲窿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)焕蹄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門逾雄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腻脏,你說我怎么就攤上這事鸦泳。” “怎么了永品?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵做鹰,是天一觀的道長。 經(jīng)常有香客問我鼎姐,道長钾麸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任炕桨,我火速辦了婚禮饭尝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘献宫。我一直安慰自己钥平,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布姊途。 她就那樣靜靜地躺著帖池,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吭净。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天肴甸,我揣著相機(jī)與錄音寂殉,去河邊找鬼。 笑死原在,一個(gè)胖子當(dāng)著我的面吹牛友扰,可吹牛的內(nèi)容都是我干的彤叉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼村怪,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秽浇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起甚负,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤柬焕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后梭域,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斑举,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年病涨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了富玷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡既穆,死狀恐怖赎懦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幻工,我是刑警寧澤励两,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站会钝,受9級(jí)特大地震影響伐蒋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜迁酸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一先鱼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奸鬓,春花似錦忌愚、人聲如沸矗漾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽母债。三九已至,卻和暖如春概页,著一層夾襖步出監(jiān)牢的瞬間敬尺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工留搔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留更胖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像却妨,于是被迫代替她去往敵國和親饵逐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359