深入解析微信插件安装技巧:Xposed系列教程篇(二)

更新:11-09 名人轶事 我要投稿 纠错 投诉

大家好,今天小编来为大家解答以下的问题,关于深入解析微信插件安装技巧:Xposed系列教程篇(二),这个很多人还不知道,现在让我们一起来看看吧!

图片旧版本实现效果

准备工作

Root相关

TWRP Recovery:强大的第三方恢复,具有官方Recovery无法实现的功能。安装Magisk Manager需要使用Magisk Manager:一款功能强大的root工具,可以替代SuperSU进行root权限管理。如果您使用VirtualXposed或Taichi或其他虚拟环境Xposed,则不需要Root。如果您使用Xposed安装程序,则需要Root权限。

关于这三者的信息请参见上一篇文章:Xposed系列Demo入门指南及源码分析(一)

反编译相关

apktool:主要用于反编译APK查看资源文件dex2jar:反编译APK获取Java源代码Jar包jd-gui:一般与dex2jar配合使用,将反编译得到的jar包拖入gui中,方便查看源代码jadx :功能强大的反编译工具,可以直接查看Java代码和资源文件。它还支持查看Smali。推荐使用jadx工具反编译。例如,从微信官网下载的32位版本的微信,会默认下载上面的weixin7016android1700_arm64.apk。

如果下载的是arm64版本,有些x86架构的模拟器不支持微信,无法正常使用,所以32位版本主要是为了方便在模拟器上调试。

图像

然后将下载的weixin7016android1700.apk拖入JadxGUI中。反编译结果如下:

图像

并且我们还可以将其保存为Gradle项目并在Android Studio或IDEA中查看。操作入口如下:

image

Hook分析相关

adb命令

adb 命令请参考https://github.com/xbdcc/CCommand。这里简单介绍一下这里需要用到的东西:

adb shell dumpsys Activity top Activity_top.txt 当然,如果你已经熟悉adb命令,想要快速输入,也可以在环境变量中设置alisa。

在控制台输入vim ~/.bash_profile,添加一行将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使环境变量生效。最后直接在控制台输入activity_top,回车即可。效果和刚才一长串命令是一样的。该命令可以输出当前Activity的信息,例如该命令捕获的变更页面Activity。信息如下:

TASK com.tencent.mm id=184

活动com.tencent.mm/.plugin.wallet.balance.ui.WalletBalanceManagerUI 1fcb6708 pid=1326

本地活动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 长端口手指-keyb/v/h -nav/h s.5mThemeChanged=0mThemeChangedFlags=0mFlipFont=0}

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{WalletBalanceManagerUI 中的247455f2{27bbdf64}}

mActivity=com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI@27bbdf64

子FragmentManager{ReportFragment 中的a6aeac0{22e48443}}:

FragmentManager 杂项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

添加了片段:

#0: ReportFragment{22e48443 #0 android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag}

FragmentManager 杂项state:

mActivity=com.tencent.mm.plugin.wallet.balance.ui.WalletBalanceManagerUI@27bbdf64

mContainer=android.app.Activity$1@386ddf3e

mCurState=5 mStateSaved=false mDestroyed=false

查看Root:

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

编舞:

mFrameScheduled=false

mLastFrameTime=16770070(150010 毫秒前)

查看层次结构:

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. 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通过辅助功能实现抢微信红包原理简单介绍 image,分析trace方法调用栈,例如下: imageimage,生成生成html格式的trace,可以分析卡顿丢帧等问题,如果有打Trace也可以在上面看出来。可以在Chrome里打开查看,如下: image

再介绍三种方便查找id值的方法

假如我们要查看id为dod的值:

通过activity_top查看

在里面找到元素对应的值,为十六进制值。如上面结果中有这样一行,可以看到id为"dod"的值为十六进制值7f091794 com.robinhood.ticker.TickerView{21299fb V.ED.... ........ 0,0-330,114 #7f091794 app:id/dod}

通过apk查看

可以把apk拖进AS中,在resources.arsc下选择你要找的id,得到的值为十六进制 image

通过jadx查看

双击resources.arsc,可以在里面搜索id,得到的值为十进制。如id为dod的值为十进制值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找到这个id的地方知道它其实是com.robinhood.ticker.TickerView这个控件, 嗯这个一看就是不是腾讯的自定义View而是用的第三方库,Github上一搜,可以知道它用的是ticker这个库, 后面可以看到钱包页面和零钱页面显示金额的也是用的这个控件。 知道了这个库我们可以看下TickerView这个类的代码,这里再推荐一个Chrome插件octotree比较方便在GitHub网页上切换文件, 如下,可以看到这里有个setText方法,里面执行了columnManager.setText(targetText);代码,而columnManager最后执行了columnManager.draw(canvas, textPaint);把文字绘制到Canvas上了, setContentDescription(text);设置了contentDescription的值,所以这就是我们能看到描述和显示金额的值一样,但是它的text属性值却为空的原因了。 image

分析支付页面MallIndexUI

首先我们观察到每次进入支付页面它的金额旁边是有个loading的,并且从钱包页面返回到支付页面也都会loading一下,那么可以猜想它可能是在onResume里面做了什么操作 (其实直接看它代码一下就能看出来,假设我们还没看代码先简单猜下)。那么我们看下它的onResume方法调用栈如下: image看到了吗?里面主要就执行了MallIndexBaseUI(MallIndexUI的父类)的onResume和自己的dbb方法,这个时候如果你不想看源码继续分析的话其实就已经可以尝试Hook跑起来看看效果了, 本着保险起见我们还是先继续看看它的源码 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应该就是打印的log方法,根据它的日志updateBalanceNum,可以知道这个方法主要是更新余额的,那么我们是不是可以手动拦截这个方法替换为自己设置的呢?我们来试试, 拿到方法的对象转为Activity,然后通过findViewById找到显示金额的控件,通过反射拿到setText并且调用赋值,或者通过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(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 "" } }) }其实刚刚在dbb方法有这样一个方法this.uDf.setMoney,通过方法名知道它是设置金额的,所以我们也可以从这里下手,该方法代码如下: public void setMoney(String str) { AppMethodBeat.i(71606); cc(str, false); AppMethodBeat.o(71606); }再来看cc这个方法,第一个if它首先是判断了如果传进来的str为null则返回,第二个if在WcPayMoneyLoadingView类里面我们可以看到this.BNi只在setFirstMoney方法里面赋值this.BNi = str;, 在reset方法里清空该值,并且进到bs类里面可以看到isNullOrNil方法如果this.BNi值为null或者字符串长度小于等于0才返回true, 所以这里其实是判断这个字段是否为空,如果不为空就直接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); }继续看if (z)这个判断,里面主要执行了removeCallbacks(this.BNk);,而this.BNk是一个Runnable对象如下, 根据日志show loading pb知道这里显示了loading Progress,设置了它为可见的,最终其实可以找到它其实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这个方法,在它执行前修改第一个参数值为你的金额"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中有setFirstMoney和setNewMoney,其实我们就可以直接Hook替换这两个方法, 而且该方法通用三个页面都有效,如下: 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里面声明的对象,再根据View Hierarchy我们知道AZo是在TickerView外面的WcPayMoneyLoadingView, 继续看this.AZo.cc,可以发现它调用的之前支付页面分析的WcPayMoneyLoadingView中的tk方法,所以我们可以直接Hook这个方法,如下: 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(0x7f091794) //id:dod的id值为0x7f091794 XposedHelpers.callMethod(view, "setText", money) xlog("Hook method tk of WalletBalanceManagerUI class and set money.") } return "" } }) }

结语

其实分析的时候可能有点复杂,并且要善于用多种工具一起分析,但是最后实现的代码很简单,整理后代码如下: 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() } } /** * 改变自定义的文本控件设置文本方法,终极boss,三个都可以 */ 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 ) /** * 在方法调用前手动修改值来改变最后显示的金额 */ 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 } } } } /** * 通过找到显示金额的控件反射拿到它的赋值方法并调用 */ private val replaceViewText = object : XC_MethodReplacement() { override fun replaceHookedMethod(param: MethodHookParam?): Any { param?.let { val activity = param.thisObject as Activity var view = activity.findViewById(0x7f091794) //id:dod的id值为0x7f091794 XposedHelpers.callMethod(view, "setText", money) xlog("find view and set text.") } return "" } }

END,本文到此结束,如果可以帮助到大家,还望关注本站哦!

用户评论

久爱不厌

好耶!期待第二期揭秘。

    有5位网友表示赞同!

蝶恋花╮

看标题就感觉很有趣,我要试试这个“装X”功能!

    有7位网友表示赞同!

♂你那刺眼的温柔

我好久没使用过Xposed框架了,这篇指南能帮我回顾一下吗?

    有15位网友表示赞同!

゛指尖的阳光丶

微信最近更新了哪些新功能啊?这篇文章会不会教我怎么利用这些新功能来装X?

    有11位网友表示赞同!

如梦初醒

之前用过Xposed框架弄一些小功能,感觉很厉害!

    有14位网友表示赞同!

七级床震

微信的官方功能太少了吧,这东西可以让我体验一下不同的玩法?

    有7位网友表示赞同!

杰克

这篇文章会不会包含哪些“装X”技巧?比如伪装自己的在线状态?

    有7位网友表示赞同!

陌颜

我比较好奇使用Xposed框架会导致什么安全性问题?

    有15位网友表示赞同!

微信名字

现在很多小软件都提供类似的功能,Xposed框架还值得一用吗?

    有7位网友表示赞同!

最迷人的危险

这篇文章会不会教我怎么安装和配置Xposed框架?

    有12位网友表示赞同!

此刻不是了i

感觉这个“装X”指南很有趣,分享一下你体验后,哪些功能最实用吧!

    有11位网友表示赞同!

来自火星的我

想问问,使用Xposed框架会不会影响微信的稳定性呢?

    有16位网友表示赞同!

念旧是个瘾。

如果这个方法风险比较高,会不会有更好的替代方案?

    有9位网友表示赞同!

心安i

学习一个新的技能,感觉有点刺激!要跟着这篇指南一起玩玩~

    有6位网友表示赞同!

淡抹丶悲伤

有没有什么安全小技巧可以让我们使用Xposed框架时更加安全?

    有16位网友表示赞同!

素颜倾城

这篇文章写得怎么样?有什么值得推荐的吗?

    有8位网友表示赞同!

花容月貌

听起来很有趣,我决定去试试这个“装X”指南!

    有6位网友表示赞同!

拥抱

好奇一下,哪些微信用户比较需要这种“装X”功能?

    有17位网友表示赞同!

几妆痕

分享一下你使用Xposed框架装X之后的故事吧,一定会很精彩!

    有9位网友表示赞同!

一样剩余

最近想尝试一些新的手机使用方法,这篇指南正好赶上了~

    有10位网友表示赞同!

【深入解析微信插件安装技巧:Xposed系列教程篇(二)】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:揭秘:这些被忽视的减肥要点,竟是你减肥失败的关键! 下一篇:雪茄与香烟:全面对比解析!