博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java反射与hook混用反射某支付的方法
阅读量:5914 次
发布时间:2019-06-19

本文共 10185 字,大约阅读时间需要 33 分钟。

反射某支付软件apk的方法 思路:

1、可以先取得某支付软件的classLoader,可以通过hook某支付软件的必须方法(如:LauncherActivity的attachBaseContext方法)来取得某支付软件classLoader;

2、取得某支付软件的classLoader,则可以查找某支付软件dex中的所有方法和变量,就反射某支付软件的方法和变量了;

注意:在activity中,反射生命周期中赋值的成员变量时,需要取得当前打开的activity的对象,再在此基础上反射对象中的成员变量。(activity类和普通类的反射区别)

比如反射PayeeQRSetMoneyActivity中的方法和变量,代码如下:

private void hookLaunch(final ClassLoader classLoader)    {        XposedHelpers.findAndHookMethod("com.***.***.quinox.LauncherActivity", classLoader,                "attachBaseContext", Context.class, new XC_MethodHook()                {                    @Override                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable                    {                        LogUtil.i("????", "start hook hookLaunch beforeHookedMethod...");                        super.beforeHookedMethod(param);                        doReflect(classLoader);                    }                    @Override                    protected void afterHookedMethod(MethodHookParam param) throws Throwable                    {                        super.afterHookedMethod(param);                    }                });    }    private void doReflect(ClassLoader classLoader)    {        try        {            //反射成员变量g            Class
aClass = classLoader.loadClass("com.***.***.payee.ui.PayeeQRSetMoneyActivity"); Object instance = aClass.newInstance(); Field field = aClass.getField("g"); field.set(instance,"1000"); LogUtil.i("????","filed value = "+field.get(instance)); //输出为“filed value = 1000” //反射方法a Method method = aClass.getDeclaredMethod("a"); method.invoke(instance); LogUtil.i("????","reflect method end..."); } catch (Exception e) { e.printStackTrace(); } }复制代码

上述代码能反射成员变量g,并改变a的值;

但是反射方法a时,抛出空指针异常,指向method.invoke(instance); 观察a方法发源码:

final void a() {        ConsultSetAmountReq v0 = new ConsultSetAmountReq();        v0.amount = this.g;        v0.desc = this.c.getUbbStr();        v0.sessionId = this.h;        new RpcRunner(new dk(this), new dj(this)).start(new Object[]{v0});    }复制代码

由于this.g能正常赋值,并且hook getUbbStr()方法的流程并未进入,因为可能是this.c空指针;最后通过反射成员变量c,确实取得null。

观察源码发现this.c在onCreate方法中初始化,所以就想通过反射onCreate使this.c的初始化工作执行完成,但是忘记了activity的生命周期,得到的结果是成员变量c仍然为空。

private static void doReflectOnCreate(ClassLoader classLoader)    {        try        {            Class
aClass = classLoader.loadClass("com.***.***.payee.ui.PayeeQRSetMoneyActivity"); Object instance = aClass.newInstance(); Method onCreate = aClass.getDeclaredMethod("onCreate", Bundle.class); LogUtil.i("????","onCreate = "+onCreate); onCreate.invoke(instance, new Bundle()); Field field = aClass.getField("c"); LogUtil.i("????","filed value = "+field.get(instance)); //反射方法a Method method = aClass.getDeclaredMethod("a"); method.invoke(instance); LogUtil.i("????","reflect method end..."); } catch (Exception e) { e.printStackTrace(); } }复制代码

onCreate.invoke(instance, new Bundle());这句话报空指针,最后听说直接反射onCreate方法是不行的,因为除了onCreate方法,还有很多系统帮忙调用的方法,一一反射调用也是很大的工程,因为就放弃了。

所以当需要反射activity的生命周期方法时,最后是打开activity让系统帮忙调用。但是不能像上述那样,使用classLoader直接loadClass,因为loadClass是重新加载一次PayeeQRSetMoneyActivity类,与当前打开的页面不处于同一个对象。因此得到的结果是:直接打开activity可以取得已赋值的成员变量viewById的值,但是loadClass反射的成员变量viewById取得的结果为空。

因此在activity中,反射生命周期中赋值的成员变量时,需要取得当前打开的activity的对象,再在此基础上反射对象中的成员变量。

private static void getCInstance(final ClassLoader classLoader)//, final Object[] objects)    {        new Thread(new Runnable()        {            @Override            public void run()            {                try                {                    Thread.sleep(200);//睡眠是为了等待页面打开                    //获取activity对象,执行生码                    Activity activity = Util.getActivity();                    if (activity != null)                    {                        Class
aClass = activity.getClass(); //获取C Field field = aClass.getDeclaredField("c"); field.setAccessible(true); Object cObj = field.get(activity); LogUtil.i("????", "end getCInstance field c = " + cObj); if (cObj != null) { Field gField = aClass.getDeclaredField("g"); gField.set(activity,"100"); LogUtil.i("????", "end setMoney = " + gField.get(activity)); Method method = aClass.getDeclaredMethod("a"); LogUtil.i("????", "invokeCreateQrCode method = " + method); method.setAccessible(true); method.invoke(activity); LogUtil.i("????", "end invokeCreateQrCode"); } } } catch (Exception e) { e.printStackTrace(); } } }).start(); } //获取栈中,paused为FALSE的activity的对象,即没有被onPause的页面对象,即当前可视的 public static Activity getActivity() { Class activityThreadClass = null; try { activityThreadClass = Class.forName("android.app.ActivityThread"); Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); Field activitiesField = activityThreadClass.getDeclaredField("mActivities"); activitiesField.setAccessible(true); Map activities = (Map) activitiesField.get(activityThread); Collection values = activities.values(); for (Object activityRecord : values) { Class activityRecordClass = activityRecord.getClass(); Field pausedField = activityRecordClass.getDeclaredField("paused"); pausedField.setAccessible(true); boolean aBoolean = pausedField.getBoolean(activityRecord); if (!aBoolean) { Field activityField = activityRecordClass.getDeclaredField("activity"); activityField.setAccessible(true); Activity activity = (Activity) activityField.get(activityRecord); return activity; } } } catch (Exception e) { e.printStackTrace(); } return null; }复制代码

此时获取到的变量c是已经赋值后的,因此再反射调用a方法的时候,不会再报空指针。

最后发现并不需要hook某支付软件取得某支付软件的classload,只要当前可视activity的对象即可反射某支付软件当前打开页面中的方法。系统提供了当前打开的activity的对象,再获得该对象的Class和classloader即可反射该activity的方法和变量。

某支付软件PayeeQRSetMoneyActivity的源码:
package com.***.***.payee.ui;import android.content.Context;import android.content.Intent;import android.os.Bundle;import com.alipay.android.hackbyte.ClassVerifier;import com.***.***.beehive.rpc.RpcRunner;import com.***.***.beehive.util.KeyBoardUtil;import com.***.***.common.logging.api.LoggerFactory;import com.***.***.commonui.inputfomatter.APMoneyFormatter;import com.***.***.commonui.widget.APButton;import com.***.***.commonui.widget.APInputBox;import com.***.***.commonui.widget.APTextView;import com.***.***.framework.app.ui.BaseActivity;import com.***.***.framework.service.common.RpcService;import com.***.***.payee.R$id;import com.***.***.payee.R$layout;import com.***.***.payee.R$string;import com.***.***.payee.util.Logger;import com.***.***.payee.util.SpmHelper;import com.alipay.transferprod.rpc.CollectMoneyRpc;import com.alipay.transferprod.rpc.req.ConsultSetAmountReq;import com.alipay.transferprod.rpc.result.ConsultSetAmountRes;public class PayeeQRSetMoneyActivity extends BaseActivity {    public static final Logger a;    protected APInputBox b;    protected APInputBox c;    protected APTextView d;    protected APButton e;    CollectMoneyRpc f;    String g;    private String h;    static {        PayeeQRSetMoneyActivity.a = Logger.a(PayeeQRSetMoneyActivity.class);    }    public PayeeQRSetMoneyActivity() {        super();        this.g = "";        if(Boolean.FALSE.booleanValue()) {            ClassVerifier.class.toString();        }    }    final void a() {        ConsultSetAmountReq v0 = new ConsultSetAmountReq();        v0.amount = this.g;        v0.desc = this.c.getUbbStr();        v0.sessionId = this.h;        new RpcRunner(new dk(this), new dj(this)).start(new Object[]{v0});    }    protected final void a(ConsultSetAmountRes arg2) {        this.runOnUiThread(new di(this, arg2));    }    protected void onCreate(Bundle arg5) {        int v3 = 2;        super.onCreate(arg5);        this.f = this.mApp.getServiceByInterface(RpcService.class.getName()).getRpcProxy(CollectMoneyRpc.class);        Intent v0 = this.getIntent();        if(v0 != null) {            try {                this.h = v0.getStringExtra("sessionId");            }            catch(Exception v0_1) {                LoggerFactory.getTraceLogger().warn(PayeeQRPayerPayResultActivity.class.getSimpleName(), ((Throwable)v0_1));            }        }        this.setContentView(R$layout.payee_qr_set_money);        this.b = this.findViewById(R$id.payee_QRmoneySetInput);        this.c = this.findViewById(R$id.payee_QRmoneySetBeiZhuInput);        this.d = this.findViewById(R$id.payee_QRAddBeiZhuLink);        this.e = this.findViewById(R$id.payee_NextBtn);        this.getWindow().setSoftInputMode(16);        this.d.setOnClickListener(new df(this));        this.e.setOnClickListener(new dg(this));        this.c.setInputName(this.getString(R$string.payee_reason), v3);        this.b.setInputName(this.getString(R$string.payee_money), v3);        this.b.addTextChangedListener(new dh(this));        this.b.setTextFormatter(new APMoneyFormatter());        this.b.getEtContent().requestFocus();        KeyBoardUtil.showSoftInput(((Context)this), this.b.getEtContent(), 0, 1);    }    protected void onPause() {        super.onPause();        SpmHelper.b("a87.b1481", this);    }    protected void onResume() {        super.onResume();        SpmHelper.a("a87.b1481", this);    }}复制代码

#### YunSoul技术分享,扫码关注微信公众号##
  • ——只要你学会了之前所不会的东西,只要今天的你强过了昨天的你,那你就一直是在进阶的路上了。

转载地址:http://ncwvx.baihongyu.com/

你可能感兴趣的文章
对于I/O流中解压中遇到的问题
查看>>
问答项目---用户注册的那些事儿(JS验证)
查看>>
Android进阶篇-百度地图获取地理信息
查看>>
返回前一页并刷新页面方法
查看>>
2.3 InnoDB 体系架构
查看>>
不定宽高垂直居中分析
查看>>
项目管理学习笔记之二.工作分解
查看>>
C# PPT 为形状设置三维效果
查看>>
js数组实现不重复插入数据
查看>>
aidl跨进程通讯
查看>>
如何确定所运行的 SQL Server 2005 的版本?
查看>>
我的友情链接
查看>>
老李分享:qtp自动化测试框架赏析-关键字自动化测试框架 2
查看>>
忙里偷闲 -- 工作随笔
查看>>
springboot报编译失败 Compilation failure
查看>>
mysqld error(一)
查看>>
Javascript延时函数
查看>>
UML类图关系大全
查看>>
Ant编译Hadoop 1.0.3的eclipse-plugin插件包
查看>>
tensorflow开发环境搭建
查看>>