裝X指南之用 Xposed 把某寶資產改爲100w

第一節

1、前言

Xposed 能幹嗎?我能夠告訴你 Root + Xposed ,真的能夠隨心所欲。而 Android 開源,爲「搞機」帶了更多的樂趣的同時,固然也引入安全性問題,部分流氓軟件在 Root 下,會盜取用戶私密信息,例如:號碼、照片、短信、密碼······,因此須要慎重使用 Root,此外本文僅做爲技術學習。php

2、Xposed 安裝

1.首先你的手機 必須 Root,關於各安卓機型的 Root 方法,請自行到對應機型論壇和貼吧找找 (注:Root 有風險,失敗可能致使手機變磚,風險自行承擔)android

2.下載「Xposed Installer」軟件並安裝,須要留意的是你的手機系統版本,不一樣版本下載對應的 apk,若是上不了該網址,可百度搜索——Xposed Installer 最新版本下載。git

  • Xposed 官方網址:

repo.xposed.info/module/de.r…github

  • Android 5.0 及以上版本的下載地址:

forum.xda-developers.com/showthread.…shell

官網說明

3.激活 Xposed 框架(確保你手機刷入了第三方 Recovery),激活後可能會使系統變的有些卡頓,但爲了技術(裝B),咱們犧牲點性能仍是值得~api

  • 點擊【安裝/更新】,選擇【Install via recovery】
  • 等待【下載】,重啓到【Recovery】模式,期間請勿操做
  • 耐心等待,刷完會自動重啓,打開【Xposed Installer】,顯示框架已激活。

Install via recovery
框架已激活

3、配置 Xposed 插件

如何配置 Android Studio 項目爲 Xposed 插件?緩存

一、配置項目 Gradle 的依賴

compileOnly 'de.robv.android.xposed:api:82'
    compileOnly 'de.robv.android.xposed:api:82:sources'
複製代碼

注:須要 compileOnly 來依賴,若是不想經過 Gradle 配置,也能夠下載 XposedBridgeApi.jar ,放到項目 libs 目錄。安全

二、配置 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:最低版本號

三、新建 Hook 入口類

該類須要實現接口 IXposedHookLoadPackage,並實現裏面關鍵方法handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam),該方法會在每一個軟件被啓動的時候回調,因此通常須要經過目標包名過濾。bash

/**
 * @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軟件");
        }
    }
}
複製代碼

四、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[0];
                    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;
    }
}
複製代碼

4、反編譯工具

一、TopActivity.apk

安裝這個工具能夠直接查看當前 App 顯示的最前 Activity,後面咱們接觸別人寫的項目,這個工具能夠很方便跟蹤代碼的入口,項目原理是經過 AccessibilityService 服務,監聽全部的界面變化,讀取當前界面 Activity,須要給軟件開啓【輔助權限】。

項目地址: github.com/109021017/a…

固然也能夠經過 Adb 命令,獲取 Dumpsys 當前 Activity 的信息:

adb shell dumpsys activity top
複製代碼

Activity信息

二、BDOpener.apk

這是 Xposed 的插件模塊,也就是說須要刷入 Xposed 框架後,才能使用該,經過安裝這個軟件,咱們可使手機安裝的軟件,變爲可調試狀態,這樣就能夠經過 Android Studio 的 Monitor 工具,查看全部的進程狀態,並 dumpsys 方法調用信息。

安裝這個軟件以後,須要在 Xposed 裏面的【模塊】裏面,把該軟件勾選後,【重啓手機】才能生效。

有人說找不到 Monitor 工具,該工具在新的 As 版本把入口取消了,咱們能夠在:C:\Users\Administrator\AppData\Local\Android\Sdk\tools\monitor.bat 目錄下找到。

開啓調試狀態以前,只能看到 AS 調試安裝的進程:

打開前

開啓調試狀態以後,能夠看到全部的進程信息:

打開後

三、jadx-gui 工具

關於這個工具的介紹,這裏我就再也不贅述,貼一篇我以爲寫得很好的博文,做者講得很詳細易懂。

Android 反編譯利器,jadx 的高級技巧:www.jianshu.com/p/e5b021df2…

jadx

注:若是出現 Jdk 錯誤,請安裝 Jdk 64 位版本。 更多技術分享,請加微信公衆號——碼農茅草屋:

第二節

1、前言

2018 年眨眼就結束,2019 年即將新年,在外工做拼搏一年,看着身邊的朋友一個個升職加薪,買房買車,你是否是很羨慕!今天看我如何一步一步改掉個人支付寶帳戶餘額的,進階成爲「百萬富翁」

雖然不是真的帳戶餘額,可是看着這個數字仍是很感人的~

0.00
100w

2、尋找 Hook 入口

一、結合 Top-Activity 獲取到當前首頁的 Activity 名稱,能夠看到截圖支付寶首頁的界面的 Activity 名稱是 com.eg.android.AlipayGphone.AlipayLogin,而後咱們打開 Jadx-gui , 並打開下載好的 alipay_wap_main.apk,反編譯出源碼

首頁

二、首頁這樣的排版,能想到這是常見的佈局形式:TabHost + Fragment。那重點去找【財富】對應的 Tab 下的 Fragment 類,打開 AlipayLogin 源碼,它是繼承 LauncherActivity ,不出意外的話,應該能夠在這個類找到下面四個 Tab 各自對應的 Fragment。

AlipayLogin

不過尷尬的是,這裏面我反覆找了下,沒能找到 Fragment 相關信息

LauncherActivity

三、經過 Monitor 去跟蹤一下里面的方法的調用流程,看看是怎麼個執行順序

在點擊【財富】按鈕前,立刻點擊 Monitor 裏的 Start Method Profiling ,等到頁面完切過去【財富頁】,而且加載數據完畢,點下中止便可,這樣就能跟蹤到這個進程的方法調用信息了。

Start Method Profiling

在 trace 裏面,重點關注包名爲 com.alipay.xxx 開頭的類的方法,能夠找到幾個可疑的方法調用,裏面有 TabHost、TabLauncherFragment、FortureWidgetGroup、FortureHomeView,最主要是 Fortune 這個單詞是財富的意思

基本能夠確定最裏面的是 FortureHomeView 這個做爲界面視圖,下個步驟就是怎麼在 FortureHomeView 找到咱們須要改的【總資產】這個佈局,還有這個佈局賦值的地方

trace

繼續跟蹤,找到 FortureHomeView.updateFortureHead() 方法,由方法名可知更新頭部,展開這個方法,看看裏面調用了什麼

FortureHomeView

點擊 AssetCardV2View.renderData() 方法名大概知道是 View 處理數據地方

AssetCardV2View.renderData()

愈來愈清晰了,繼續找下去。看到裏面的 AssetHeaderV2View.setData() 知道是頭部的設置數據

AssetHeaderV2View

展開裏面的 AssetHeaderV2View.setData() 看到有調用 TextView 的 setText() 方法,還有一個 NumRunningTextView 的 setRunningText() ,這裏結合咱們平時看到的界面效果,這是個自定義的帶動畫效果的View,能夠猜想是【昨日收益】的 TextView,由於受益爲了更好地用戶體驗,會帶滾動效果,平時有留意就知道了。

昨日收益

知道具體是 AssetHeaderV2View,在設置界面的值,如今去看看它的源碼是咋回事。

四、第一個框框裏面,有個 string 的命名是 hide_status_text,我以爲就是咱們隱藏資產的那個【*】符號

而後再看看條件 else if(assetsCardModel != null) 後面執行的代碼,在第二個框裏面,發現 setRunningText() 這個方法,並且方法有個參數是:totalYesterdayProfitView ,意思是昨天總收益,那這裏應該就是【昨日收益】設值的地方

那就是說前一個setText() 方法,若是沒有錯誤的話,就是設值【總資產】的地方,設值變量爲 latestTotalView

AssetHeaderV2View.setData()

爲了確保沒有錯誤,跟一下變量 a 和變量 b 具體是什麼類型,能夠看到一個是 AUAutoResizeTextView 另外一個 NumRunningTextView,這應該是自定義的控件,爲啥總資產要定義爲自動調整大小的View呢?想一下若是寫死寬度的話或者字體大小的,就兼容不了每一個人的資產數目,如:1000000 和 1 長度的區別。

a和b變量

AssetsCardModel 應該是存儲用戶資產信息的 Model 類,框框裏面的變量,就是存儲着最新的【總資產】信息

AssetsCardModel

好的,到這裏分析完了,總結一下具體是怎麼個流程:

LauncherActivity -> TabHost -> TabLauncherFragment -> FortureHomeView.updateFortureHead() -> AssetCardV2View.renderData() -> AssetHeaderV2View.setData() -> AUAutoResizeTextView.setText()

流程圖

3、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[0];
                        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);
                    }
                });
            }

        }
    }
}
複製代碼

相關閱讀:

更多技術分享,請加微信公衆號——碼農茅草屋:

碼農茅草屋
相關文章
相關標籤/搜索