java hook 和反射_Java反射与hook混用反射某支付的方法

反射某支付软件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 extends Activity> 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技术分享,扫码关注微信公众号

——只要你学会了之前所不会的东西,只要今天的你强过了昨天的你,那你就一直是在进阶的路上了。

353058.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值