用Xposed把支付宝资产改成100w
本帖最后由 behindeye 于 2020-1-20 14:05 编辑# 第一部分
## 一、前言
Xposed 能干嘛?我可以告诉你 Root + Xposed ,真的可以为所欲为。而 Android 开源,为“搞机”带了更多的乐趣的同时,当然也引入安全性问题,部分流氓软件在 Root 下,会盗取用户私密信息,例如:号码、照片、短信、密码······,所以需要慎重使用 Root,此外本文仅作为技术学习。
## 二、Xposed 安装
1.首先你的手机 **必须 Root**,关于各安卓机型的 Root 方法,请自行到对应机型论坛和贴吧找找 **(注:Root 有风险,失败可能导致手机变砖,风险自行承担)**
2.**下载「Xposed Installer」软件并安装**,需要留意的是你的手机系统版本,不同版本下载对应的 apk,如果上不了该网址,可百度搜索——Xposed Installer 最新版本下载。
- Xposed 官方网址:
> (https://repo.xposed.info/module/de.robv.android.xposed.installer)
- Android 5.0 及以上版本的下载地址:
> (http://forum.xda-developers.com/showthread.php?t=3034811)
![官网说明](https://ws1.sinaimg.cn/large/9fd1f8e3gy1fz3lvisgn8j20tb0fr403.jpg)
3.**激活 Xposed 框架**(确保你手机刷入了第三方 Recovery),激活后可能会使系统变的有些**卡顿**,但为了技术(装B),我们牺牲点性能还是值得~
- 点击【安装/更新】,选择【Install via recovery】
- 等待【下载】,重启到【Recovery】模式,期间请勿操作
- 耐心等待,刷完会自动重启,打开【Xposed Installer】,显示框架已激活。
!(https://ws1.sinaimg.cn/large/9fd1f8e3gy1fz3mpxidb5j208p0gbwey.jpg)![框架已激活](https://ws1.sinaimg.cn/large/9fd1f8e3gy1fz3mqcyjxij208r0gbwf8.jpg)
## 三、配置 Xposed 插件
如何配置 Android Studio 项目为 Xposed 插件?
#### 1、配置项目 Gradle 的依赖
```
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
```
注:需要 compileOnly 来依赖,如果不想通过 Gradle 配置,也可以下载 XposedBridgeApi.jar ,放到项目 libs 目录。
#### 2、配置 AndroidManifest.xml
```
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="XXX插件" />
<meta-data
android:name="xposedminversion"
android:value="89" />
```
- xposedmodule:是否配置为 Xposed 插件,设置为 true
- xposeddescription:模块名称
- xposedminversion:最低版本号
#### 3、新建 Hook 入口类
该类需要实现接口 `IXposedHookLoadPackage`,并实现里面关键方法`handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam)`,该方法会在每个软件被启动的时候回调,所以一般需要通过目标包名过滤。
```
/**
* @author zhicheng.chen
*/
public class TargetHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
//通过目标包名过滤
if (lpparam.packageName.equals("com.xxx.xxx")) {
XposedBridge.log("启动了xxx软件");
}
}
}
```
#### 4、Xposed 免重启调试
Xposed插件每次代码改动,都需要重启手机才能生效,有时候重启一次还不生效(我的手机有一次重启 3 次,才看到生效,还好是公司测试机,不心疼),所以代码最好写上相关 Log 信息,来看代码生效没。
`XposedBridge.log("启动了xxx软件");`
##### 不过这里分享一个免重启调试的方法,方法来自网上,感谢 DX :
```
/**
* @author DX
* 这种方案建议只在开发调试的时候使用,因为这将损耗一些性能(需要额外加载apk文件),调试没问题后,直接修改xposed_init文件为正确的类即可
* 可以实现免重启,由于存在缓存,需要杀死宿主程序以后才能生效
* 这种免重启的方式针对某些特殊情况的hook无效
* 例如我们需要implements IXposedHookZygoteInit,并将自己的一个服务注册为系统服务,这种就必须重启了
* Created by DX on 2017/10/4.
*/
public class HookLoader2 implements IXposedHookLoadPackage {
//按照实际使用情况修改下面几项的值
/**
* 当前Xposed模块的包名,方便寻找apk文件
*/
private final String modulePackage = "com.xxx.plugin";
/**
* 宿主程序的包名(允许多个),过滤无意义的包名,防止无意义的apk文件加载
*/
private static List<String> hostAppPackages = new ArrayList<>();
static {
// TODO: Add the package name of application your want to hook!
hostAppPackages.add("com.eg.android.AlipayGphone");
hostAppPackages.add("com.xxx.plugin");
}
/**
* 实际hook逻辑处理类
*/
private final String handleHookClass = TargetHook.class.getName();
/**
* 实际hook逻辑处理类的入口方法
*/
private final String handleHookMethod = "handleLoadPackage";
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (hostAppPackages.contains(loadPackageParam.packageName)) {
//将loadPackageParam的classloader替换为宿主程序Application的classloader,解决宿主程序存在多个.dex文件时,有时候ClassNotFound的问题
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context=(Context) param.args;
loadPackageParam.classLoader = context.getClassLoader();
invokeHandleHookMethod(context,modulePackage, handleHookClass, handleHookMethod, loadPackageParam);
}
});
}
}
/**
* 安装app以后,系统会在/data/app/下备份了一份.apk文件,通过动态加载这个apk文件,调用相应的方法
* 这样就可以实现,只需要第一次重启,以后修改hook代码就不用重启了
* @param context context参数
* @param modulePackageName 当前模块的packageName
* @param handleHookClass 指定由哪一个类处理相关的hook逻辑
* @param loadPackageParam传入XC_LoadPackage.LoadPackageParam参数
* @throws Throwable 抛出各种异常,包括具体hook逻辑的异常,寻找apk文件异常,反射加载Class异常等
*/
private void invokeHandleHookMethod(Context context, String modulePackageName, String handleHookClass, String handleHookMethod, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
File apkFile=findApkFile(context,modulePackageName);
if (apkFile==null){
throw new RuntimeException("寻找模块apk失败");
}
//加载指定的hook逻辑处理类,并调用它的handleHook方法
PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader());
Class<?> cls = Class.forName(handleHookClass, true, pathClassLoader);
Object instance = cls.newInstance();
Method method = cls.getDeclaredMethod(handleHookMethod, XC_LoadPackage.LoadPackageParam.class);
method.invoke(instance, loadPackageParam);
}
/**
* 根据包名构建目标Context,并调用getPackageCodePath()来定位apk
* @param context context参数
* @param modulePackageName 当前模块包名
* @return return apk file
*/
private File findApkFile(Context context, String modulePackageName){
if (context==null){
return null;
}
try {
Context moudleContext = context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
String apkPath=moudleContext.getPackageCodePath();
return new File(apkPath);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
```
## 四、反编译工具
#### 1、TopActivity.apk
安装这个工具可以直接查看当前 App 显示的最前 Activity,后面我们接触别人写的项目,这个工具可以很方便跟踪代码的入口,项目原理是通过 `AccessibilityService` 服务,监听所有的界面变化,读取当前界面 Activity,需要给软件开启【辅助权限】。
> 项目地址:
(https://github.com/109021017/android-TopActivity)
当然也可以通过 Adb 命令,获取 Dumpsys 当前 Activity 的信息:
```
adb shell dumpsys activity top
```
!(https://upload-images.jianshu.io/upload_images/2578395-859d265d59ca172c.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/319/format/webp)
#### 2、BDOpener.apk
这是 Xposed 的插件模块,也就是说需要刷入 Xposed 框架后,才能使用该,通过安装这个软件,我们可以使手机安装的软件,变为**可调试状态**,这样就可以**通过 Android Studio 的 Monitor 工具,查看所有的进程状态**,并 dumpsys 方法调用信息。
安装这个软件之后,需要在 Xposed 里面的【模块】里面,把该软件勾选后,【重启手机】才能生效。
有人说找不到 Monitor 工具,该工具在新的 As 版本把入口取消了,我们可以在:`C:\Users\Administrator\AppData\Local\Android\Sdk\tools\monitor.bat` 目录下找到。
开启调试状态之前,只能看到 AS 调试安装的进程:
![打开前](https://upload-images.jianshu.io/upload_images/2578395-766899778ad6c23d.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/399/format/webp)
##### 开启调试状态之后,可以看到所有的进程信息:
![打开后](https://upload-images.jianshu.io/upload_images/2578395-4dab9a75b048ada4.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/405/format/webp)
#### 3、jadx-gui 工具
关于这个工具的介绍,这里我就不再赘述,贴一篇我觉得写得很好的博文,作者讲得很详细易懂。
>Android 反编译利器,jadx 的高级技巧:(https://www.jianshu.com/p/e5b021df2170)
!(https://upload-images.jianshu.io/upload_images/2578395-c5c153d727b307e9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/956/format/webp)
注:如果出现 Jdk 错误,请安装 Jdk 64 位版本。
# 第二部分
![我用Xposed把支付宝资产改成100万](https://ws1.sinaimg.cn/large/9fd1f8e3gy1fz5884z1osj20p00an0up.jpg)
## 一、前言
2018 年眨眼就结束,2019 年即将新年,在外工作拼搏一年,看着身边的朋友一个个升职加薪,买房买车,你是不是很羡慕!今天看我如何一步一步改掉我的支付宝账户余额的,**进阶成为「百万富翁」**
虽然不是真的账户余额,但是看着这个数字还是很感人的~
!(https://upload-images.jianshu.io/upload_images/2578395-2101cfcde6319d80.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/313/format/webp)!(https://upload-images.jianshu.io/upload_images/2578395-7a462bd0e2fee1cd.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/315/format/webp)
## 二、寻找 Hook 入口
1、结合 `Top-Activity` 获取到当前首页的 Activity 名称,可以看到截图支付宝首页的界面的 Activity 名称是 `com.eg.android.AlipayGphone.AlipayLogin`,然后我们打开 Jadx-gui , 并打开下载好的 `alipay_wap_main.apk`,反编译出源码
![首页](https://upload-images.jianshu.io/upload_images/2578395-1bc1f8be1fbcb5b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/314/format/webp)
2、首页这样的排版,能想到这是常见的布局形式:`TabHost + Fragment`。那重点去找【财富】对应的 Tab 下的 Fragment 类,打开 AlipayLogin 源码,它是继承 `LauncherActivity` ,不出意外的话,应该可以在这个类找到下面四个 Tab 各自对应的 Fragment。
!(https://upload-images.jianshu.io/upload_images/2578395-aabf65cf7607029f.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/889/format/webp)
不过尴尬的是,这里面我反复找了下,没能找到 Fragment 相关信息
!(https://upload-images.jianshu.io/upload_images/2578395-69ebe63e43dca264.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/380/format/webp)
3、通过 Monitor 去跟踪一下里面的方法的调用流程,看看是怎么个执行顺序
在点击【财富】按钮前,马上点击 `Monitor` 里的 `Start Method Profiling` ,等到页面完切过去【财富页】,并且加载数据完毕,点下停止即可,这样就能跟踪到这个进程的方法调用信息了。
!(https://upload-images.jianshu.io/upload_images/2578395-e130aff28209b20d.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/401/format/webp)
在 trace 里面,重点关注包名为 `com.alipay.xxx` 开头的类的方法,可以找到几个可疑的方法调用,里面有 `TabHost、TabLauncherFragment、FortureWidgetGroup、FortureHomeView`,最主要是 Fortune 这个单词是财富的意思
基本可以肯定最里面的是 `FortureHomeView` 这个作为界面视图,下个步骤就是怎么在 `FortureHomeView` 找到我们需要改的【总资产】这个布局,还有这个布局赋值的地方
!(https://upload-images.jianshu.io/upload_images/2578395-3b4b262764bf5200.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/931/format/webp)
继续跟踪,找到 `FortureHomeView.updateFortureHead()` 方法,由方法名可知更新头部,展开这个方法,看看里面调用了什么
!(https://upload-images.jianshu.io/upload_images/2578395-281c52f457a07c21.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/931/format/webp)
点击 `AssetCardV2View.renderData()` 方法名大概知道是 View 处理数据地方
!(https://upload-images.jianshu.io/upload_images/2578395-37121d989d51342c.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/931/format/webp)
越来越清晰了,继续找下去。看到里面的 `AssetHeaderV2View.setData()` 知道是头部的设置数据
!(https://upload-images.jianshu.io/upload_images/2578395-702e748a150a801e.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/931/format/webp)
展开里面的 `AssetHeaderV2View.setData()` 看到有调用 TextView 的 setText() 方法,还有一个 NumRunningTextView 的 setRunningText() ,这里结合我们平时看到的界面效果,这是个自定义的带动画效果的View,可以猜测是【昨日收益】的 TextView,因为受益为了更好地用户体验,会带滚动效果,平时有留意就知道了。
![昨日收益](https://upload-images.jianshu.io/upload_images/2578395-bda7f4b69dec314f.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/312/format/webp)
知道具体是 `AssetHeaderV2View`,在设置界面的值,现在去看看它的**源码**是咋回事。
4、第一个框框里面,有个 string 的命名是 `hide_status_text`,我觉得就是我们隐藏资产的那个【*】符号
然后再看看条件 `else if(assetsCardModel != null)` 后面执行的代码,在第二个框里面,发现 `setRunningText()` 这个方法,而且方法有个参数是:`totalYesterdayProfitView` ,意思是昨天总收益,那这里应该就是【昨日收益】设值的地方
那就是说前一个`setText()` 方法,如果没有错误的话,就是设值【总资产】的地方,设值变量为 `latestTotalView`。
!(https://upload-images.jianshu.io/upload_images/2578395-ea6e0c51714a9e99.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp)
为了确保没有错误,跟一下变量 a 和变量 b 具体是什么类型,可以看到一个是 `AUAutoResizeTextView` 另一个 `NumRunningTextView`,这应该是自定义的控件,为啥总资产要定义为自动调整大小的View呢?想一下如果写死宽度的话或者字体大小的,就兼容不了每个人的资产数目,如:1000000 和 1 长度的区别。
!(https://upload-images.jianshu.io/upload_images/2578395-c95fa10348ebbf00.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp)
`AssetsCardModel` 应该是存储用户资产信息的 Model 类,框框里面的变量,就是存储着最新的【总资产】信息
!(https://upload-images.jianshu.io/upload_images/2578395-9fe2d1a0bf146371.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/407/format/webp)
好的,到这里分析完了,总结一下具体是怎么个流程:
>**LauncherActivity -> TabHost -> TabLauncherFragment -> FortureHomeView.updateFortureHead() -> AssetCardV2View.renderData() -> AssetHeaderV2View.setData() -> AUAutoResizeTextView.setText()**
![流程图](https://upload-images.jianshu.io/upload_images/2578395-393fcb01246ca133.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/560/format/webp)
## 三、Hook 代码
现在只需要对 `AssetHeaderV2View.setData()` 加工处理,在调用这个方法之前,对参数进行更改,通过反射把里面的 `latestTotalView`,改成你想要的金额。
```
/**
* @author zhicheng.chen
* @date 2018/11/26
*/
public class AlipayHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("com.eg.android.AlipayGphone")) {
XposedBridge.log("load alipay");
ClassLoader classLoader = lpparam.classLoader;
Class<?> aClass = classLoader.loadClass("com.alipay.android.render.engine.viewbiz.AssetsHeaderV2View");
Class<?> aClass2 = classLoader.loadClass("com.alipay.android.render.engine.model.AssetsCardModel");
if (aClass != null) {
XposedHelpers.findAndHookMethod(aClass, "setData", aClass2, boolean.class, boolean.class, boolean.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Object arg = param.args;
try {
Log.w("czc", arg.getClass().getField("latestTotalView").get(arg).toString());
arg.getClass().getField("latestTotalView").set(arg,"1000000.00");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
}
}
}
}
```
**相关阅读:**
- [装X指南之Xposed安装与配置](https://www.jianshu.com/p/aa2ada7bd637) 第一次接触 Xposed 是看 尼古拉斯·赵四 的文章,感谢四哥的无私分享,第一次写文,不足之处,还请原谅 behindeye 发表于 2019-1-14 10:46
第一次接触 Xposed 是看 尼古拉斯·赵四 的文章,感谢四哥的无私分享,第一次写文,不足之处,还请原谅
感谢各位的支持{:1_893:},没想到第一次写文,得到那么多的关注。在这里统一回复一下各位的评论:
1、Ps 肯定是最快装 X 的方式,本文是技术分享,抛砖引玉的意思;
2、关于网上有相关的 Xposed 插件【文本自定义】,这个原理也是比较简单的,只是 Hook 系统的setText() 方法
3、里面的钱不能转账,不能转账,哈哈:lol 直接PS吧哈哈 那样简单点 有啥用你又提不出来! 还有种方法改一百万,用 Photoshop 改 {:1_918:} xiaomingtt 发表于 2019-1-14 10:31
能提现吗??
当然不能,哈哈 能提现吗??{:1_904:} 如果能提现出来就好了-手动滑稽 {:1_937:}大佬,大佬。完全看不懂,我还是PS吧。。。 {:301_998:}大佬大佬