behindeye 发表于 2019-1-14 10:23

用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)

behindeye 发表于 2019-1-14 10:46

第一次接触 Xposed 是看 尼古拉斯·赵四 的文章,感谢四哥的无私分享,第一次写文,不足之处,还请原谅

behindeye 发表于 2019-1-15 11:22

behindeye 发表于 2019-1-14 10:46
第一次接触 Xposed 是看 尼古拉斯·赵四 的文章,感谢四哥的无私分享,第一次写文,不足之处,还请原谅

感谢各位的支持{:1_893:},没想到第一次写文,得到那么多的关注。在这里统一回复一下各位的评论:
1、Ps 肯定是最快装 X 的方式,本文是技术分享,抛砖引玉的意思;
2、关于网上有相关的 Xposed 插件【文本自定义】,这个原理也是比较简单的,只是 Hook 系统的setText() 方法
3、里面的钱不能转账,不能转账,哈哈:lol

duguquba 发表于 2019-1-14 10:51

直接PS吧哈哈 那样简单点

王惟珍 发表于 2019-1-14 10:51

有啥用你又提不出来!

不懂破解 发表于 2019-1-14 10:48

还有种方法改一百万,用 Photoshop 改 {:1_918:}

behindeye 发表于 2019-1-14 10:38

xiaomingtt 发表于 2019-1-14 10:31
能提现吗??

当然不能,哈哈

xiaomingtt 发表于 2019-1-14 10:31

能提现吗??{:1_904:}

kisa 发表于 2019-1-14 10:46

如果能提现出来就好了-手动滑稽

庸世俗人罢勒 发表于 2019-1-14 10:47

{:1_937:}大佬,大佬。完全看不懂,我还是PS吧。。。

tuesday37 发表于 2019-1-14 10:52

{:301_998:}大佬大佬
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 用Xposed把支付宝资产改成100w