一、前言
前不久在吾爱论坛上看见有大佬搞了个《 XPosed插件自动生成支付宝收款二维码 》,我正好想要在网上卖点东西,如果我自己搞了个支付宝收款、转账监控的话,那是不是就能节约大量我核实订单这些时间了?答案当然是能的,于是我就开干了。
PS:许多人留言需要成品,最后我还准备放上成品给大家看。
二、突破口
首先收款和转账都是有notification通知的,于是我准备从这里入手。但是大家都是知道,这种收款什么的都是新开一个push进程然后IPC和主进程通讯。如果用monitor来抓取方法栈堆效果并不是很明显,而我则决定打印log和monitor双管齐下。
首先我们用Jadx打开alipay,随便找一个Java类打开查看他们的日志打印工具。
大家可以很清楚的看见LoggerFactory这个类,而熟悉Android开发设计模式的人都知道这是工厂模式,点开一看,下面封装了许多的logger。
[Java] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[backcolor=rgb(27, 36, 38) !important][color=white !important][color=white !important]?
public static final String TAG = "LoggerFactory";
private static ProcessInfo a = new NullProcessInfo();
private static DeviceProperty b = new NullDeviceProperty();
private static LogContext c = new NullLogContext();
private static TraceLogger d = new NullTraceLogger();
private static BehavorLogger e = new NullBehavorLogger();
private static MonitorLogger f = new NullMonitorLogger();
private static EventLogger g = new NullEventLogger();
private static AtomicBoolean h = new AtomicBoolean(false);
每个对象的具体作用是什么我就不描述了,可以自行查看。但是alipay打印log正是用的TraceLogger,于是我们就自需要hook住TraceLogger这个类就行。接下来我们看下TraceLogger的代码。
[Java] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[backcolor=rgb(27, 36, 38) !important][color=white !important][color=white !important]?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class NullTraceLogger implements TraceLogger {
private NullTraceLogger() {
}
public void debug(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void error(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void error(String str, String str2, Throwable th) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void error(String str, Throwable th) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void info(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void print(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void print(String str, Throwable th) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void verbose(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void warn(String str, String str2) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void warn(String str, String str2, Throwable th) {
new IllegalMonitorStateException("need invoke bind before use");
}
public void warn(String str, Throwable th) {
new IllegalMonitorStateException("need invoke bind before use");
}
}
很简单的方法,经过我的查看我们只需要hook参数为两个string的方法就行,而含有throwable参数的多半是为了打印错误信息,这不是我们所需要的。
你以为到这里就结束了吗?当然不是,因为我发现alipay居然还有一个打印的log的。找寻方法呢很简单跟上面差不多,我就不讲了,直接上hook代码。
[Java] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[backcolor=rgb(27, 36, 38) !important][color=white !important][color=white !important]?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.yymjr.android.xposedpay;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
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;
public class MainHook implements IXposedHookLoadPackage {
private final static String TAG = "XposedPay-MainHook";
private Class<?> NullTraceLoggerClazz;
private Class<?> LogOutClazz;
private Class<?> H5LogClazz;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.eg.android.AlipayGphone")) return;
Log.d(TAG, "Hook alipay begin......");
XposedHelpers.findAndHookMethod("com.alipay.mobile.quinox.LauncherApplication",lpparam.classLoader, "attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook attachBaseContext successful......" );
ClassLoader classLoader = ((Context) param.args[0]).getClassLoader();
try {
NullTraceLoggerClazz = classLoader.loadClass("com.alipay.mobile.common.logging.api.LoggerFactory$NullTraceLogger");
LogOutClazz = classLoader.loadClass("com.alipay.pushsdk.util.log.LogUtil");
findAndookNullTraceLogger("debug");
findAndookNullTraceLogger("error");
findAndookNullTraceLogger("info");
findAndookNullTraceLogger("print");
findAndookNullTraceLogger("verbose");
findAndookNullTraceLogger("warn");
findAndHookMethod(LogOutClazz,"LogOut", int.class, String.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook LogUtil.LogOut successful.....");
Log.d(TAG, param.args[1].toString());
Log.d(TAG, param.args[2].toString());
}
});
findAndHookLogUtil("d");
findAndHookLogUtil("e");
findAndHookLogUtil("i");
findAndHookLogUtil("w");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
});
}
private void findAndHookLogUtil(final String methodName){
findAndHookMethod(LogOutClazz, methodName, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook LogUtil."+methodName+" successful....." );
Log.d(TAG, param.args[0].toString());
}
});
}
private void findAndookNullTraceLogger(final String methodName){
findAndHookMethod(NullTraceLoggerClazz, methodName, String.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook NullTraceLogger."+methodName+" successful....." );
Log.d(TAG, param.args[0].toString() );
Log.d(TAG, param.args[1].toString() );
}
});
}
private void findAndHookMethod(Class clazz, String methodName, Object... parameterTypesAndCallback){
XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback);
}
}
这个里有些知识点需要讲解,首先就是第一个hook的是 LauncherApplication 的 attachBaseContext ,因为alipay是多dex,采用multiDex加载的,如果直接hook,将会爆出classnotfound的错误,只有拿到alipay的contextx去hook才不会。这里我是CV了 @XOR 的代码。PS:我从不写代码,我直接代码的搬运工!手动滑稽。
当然还有第二种方法,那就是hook住Class.loadClass,拿到Class,这样子用class.getname来取出你想要的class。但是不知道为什么这种办法在alipay里面getname会爆nullpoity错误,所以最终我就采用了这个办法。
好的,接下来我们看一下日志。<数据很多,我只选的其中最重要的>
这里清晰的打印很多数据,我们json格式化下。
重要数据部分我都在图片里面标明了。当然还有个最重要的东西就是monitor。抓取的方法我就不说了,自行操作,接下来我说下结论。
三、结论
自行操作,接下来我说下结论。
1.push获取到数据交给amnet处理。
2.amnet数据处理后IPC通讯
3.交给主进程PushDirectMainProcessService处理。
4.PushDirectMainProcessService进行判断后交给PushNoticeDisplay处理。
5.PushNoticeDisplay,这里就是实现notification通知的地方了。
四、成品
[Java] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[backcolor=rgb(27, 36, 38) !important][color=white !important][color=white !important]?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.yymjr.android.xposedpay;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
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;
public class MainHook implements IXposedHookLoadPackage {
private final static String TAG = "XposedPay-MainHook";
private Bundle PushNoticeDisplayBundle = null;
private Class<?> PushNoticeDisplayClazz;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.eg.android.AlipayGphone")) return;
Log.d(TAG, "Hook alipay begin......");
XposedHelpers.findAndHookMethod("com.alipay.mobile.quinox.LauncherApplication",lpparam.classLoader, "attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook attachBaseContext successful......" );
ClassLoader classLoader = ((Context) param.args[0]).getClassLoader();
try {
PushNoticeDisplayClazz = classLoader.loadClass("com.alipay.mobile.rome.pushservice.integration.d");
XposedHelpers.findAndHookConstructor(PushNoticeDisplayClazz, Context.class, Bundle.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.d(TAG, "Hook PushNoticeDispaly.d successful......");
PushNoticeDisplayBundle = (Bundle) param.args[1];
if (PushNoticeDisplayBundle == null) return;
LogPushNoticeDisplay("push_show_title");
LogPushNoticeDisplay("push_show_text");
JSONObject jsonObject_data = new JSONObject(PushNoticeDisplayBundle.getString("push_msg_data"));
JSONObject paramsJsonObject = jsonObject_data.getJSONObject("params");
Log.d(TAG, "订单:"+paramsJsonObject.getString("tradeNO"));
JSONObject jsonObject_ext = new JSONObject(PushNoticeDisplayBundle.getString("push_show_ext"));
Log.d(TAG, "支付金额:"+jsonObject_ext.getString("soundValue"));
}
});
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
});
}
private void LogPushNoticeDisplay(String PushExtConstants){
Log.d(TAG, PushNoticeDisplayBundle.getString(PushExtConstants));
}
private void findAndHookMethod(Class clazz, String methodName, Object... parameterTypesAndCallback){
XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback);
}
}
该版本仅仅支持个人收款。
毕竟涉及alipay,所以我就没有一步步的讲解,更是省略了许多,这也是无奈之举。但是想要做的同学通过日志其实是能自己独立完成的。一点后言吧。
1.收款码收款,是没有订单信息的,用过收款码的同学都知道点开notification直接就是进入一个webview,如果真的想要拿到订单信息的话,就开启H5log ,把网页数据给拿出来,只不过比较麻烦。
2.转账的话你点开也是一个webview,但是他的url中带的有订单信息,所以能直接得到订单。
3.备注这个我暂时是没有发现,估计只能是在webview里面查看。