Xposed 能幹嗎?我能夠告訴你 Root + Xposed ,真的能夠隨心所欲。而 Android 開源,爲「搞機」帶了更多的樂趣的同時,固然也引入安全性問題,部分流氓軟件在 Root 下,會盜取用戶私密信息,例如:號碼、照片、短信、密碼······,因此須要慎重使用 Root,此外本文僅做爲技術學習。php
1.首先你的手機 必須 Root,關於各安卓機型的 Root 方法,請自行到對應機型論壇和貼吧找找 (注:Root 有風險,失敗可能致使手機變磚,風險自行承擔)android
2.下載「Xposed Installer」軟件並安裝,須要留意的是你的手機系統版本,不一樣版本下載對應的 apk,若是上不了該網址,可百度搜索——Xposed Installer 最新版本下載。git
3.激活 Xposed 框架(確保你手機刷入了第三方 Recovery),激活後可能會使系統變的有些卡頓,但爲了技術(裝B),咱們犧牲點性能仍是值得~api
如何配置 Android Studio 項目爲 Xposed 插件?緩存
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
複製代碼
注:須要 compileOnly 來依賴,若是不想經過 Gradle 配置,也能夠下載 XposedBridgeApi.jar ,放到項目 libs 目錄。安全
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="XXX插件" />
<meta-data
android:name="xposedminversion"
android:value="89" />
複製代碼
該類須要實現接口 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插件每次代碼改動,都須要重啓手機才能生效,有時候重啓一次還不生效(個人手機有一次重啓 3 次,纔看到生效,還好是公司測試機,不心疼),因此代碼最好寫上相關 Log 信息,來看代碼生效沒。微信
XposedBridge.log("啓動了xxx軟件");
/**
* @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;
}
}
複製代碼
安裝這個工具能夠直接查看當前 App 顯示的最前 Activity,後面咱們接觸別人寫的項目,這個工具能夠很方便跟蹤代碼的入口,項目原理是經過 AccessibilityService
服務,監聽全部的界面變化,讀取當前界面 Activity,須要給軟件開啓【輔助權限】。
項目地址: github.com/109021017/a…
固然也能夠經過 Adb 命令,獲取 Dumpsys 當前 Activity 的信息:
adb shell dumpsys activity top
複製代碼
這是 Xposed 的插件模塊,也就是說須要刷入 Xposed 框架後,才能使用該,經過安裝這個軟件,咱們可使手機安裝的軟件,變爲可調試狀態,這樣就能夠經過 Android Studio 的 Monitor 工具,查看全部的進程狀態,並 dumpsys 方法調用信息。
安裝這個軟件以後,須要在 Xposed 裏面的【模塊】裏面,把該軟件勾選後,【重啓手機】才能生效。
有人說找不到 Monitor 工具,該工具在新的 As 版本把入口取消了,咱們能夠在:C:\Users\Administrator\AppData\Local\Android\Sdk\tools\monitor.bat
目錄下找到。
開啓調試狀態以前,只能看到 AS 調試安裝的進程:
關於這個工具的介紹,這裏我就再也不贅述,貼一篇我以爲寫得很好的博文,做者講得很詳細易懂。
Android 反編譯利器,jadx 的高級技巧:www.jianshu.com/p/e5b021df2…
注:若是出現 Jdk 錯誤,請安裝 Jdk 64 位版本。 更多技術分享,請加微信公衆號——碼農茅草屋:
2018 年眨眼就結束,2019 年即將新年,在外工做拼搏一年,看着身邊的朋友一個個升職加薪,買房買車,你是否是很羨慕!今天看我如何一步一步改掉個人支付寶帳戶餘額的,進階成爲「百萬富翁」
雖然不是真的帳戶餘額,可是看着這個數字仍是很感人的~
一、結合 Top-Activity
獲取到當前首頁的 Activity 名稱,能夠看到截圖支付寶首頁的界面的 Activity 名稱是 com.eg.android.AlipayGphone.AlipayLogin
,而後咱們打開 Jadx-gui , 並打開下載好的 alipay_wap_main.apk
,反編譯出源碼
二、首頁這樣的排版,能想到這是常見的佈局形式:TabHost + Fragment
。那重點去找【財富】對應的 Tab 下的 Fragment 類,打開 AlipayLogin 源碼,它是繼承 LauncherActivity
,不出意外的話,應該能夠在這個類找到下面四個 Tab 各自對應的 Fragment。
不過尷尬的是,這裏面我反覆找了下,沒能找到 Fragment 相關信息
三、經過 Monitor 去跟蹤一下里面的方法的調用流程,看看是怎麼個執行順序
在點擊【財富】按鈕前,立刻點擊 Monitor
裏的 Start Method Profiling
,等到頁面完切過去【財富頁】,而且加載數據完畢,點下中止便可,這樣就能跟蹤到這個進程的方法調用信息了。
在 trace 裏面,重點關注包名爲 com.alipay.xxx
開頭的類的方法,能夠找到幾個可疑的方法調用,裏面有 TabHost、TabLauncherFragment、FortureWidgetGroup、FortureHomeView
,最主要是 Fortune 這個單詞是財富的意思
基本能夠確定最裏面的是 FortureHomeView
這個做爲界面視圖,下個步驟就是怎麼在 FortureHomeView
找到咱們須要改的【總資產】這個佈局,還有這個佈局賦值的地方
繼續跟蹤,找到 FortureHomeView.updateFortureHead()
方法,由方法名可知更新頭部,展開這個方法,看看裏面調用了什麼
點擊 AssetCardV2View.renderData()
方法名大概知道是 View 處理數據地方
愈來愈清晰了,繼續找下去。看到裏面的 AssetHeaderV2View.setData()
知道是頭部的設置數據
展開裏面的 AssetHeaderV2View.setData()
看到有調用 TextView 的 setText() 方法,還有一個 NumRunningTextView 的 setRunningText() ,這裏結合咱們平時看到的界面效果,這是個自定義的帶動畫效果的View,能夠猜想是【昨日收益】的 TextView,由於受益爲了更好地用戶體驗,會帶滾動效果,平時有留意就知道了。
知道具體是 AssetHeaderV2View
,在設置界面的值,如今去看看它的源碼是咋回事。
四、第一個框框裏面,有個 string 的命名是 hide_status_text
,我以爲就是咱們隱藏資產的那個【*】符號
而後再看看條件 else if(assetsCardModel != null)
後面執行的代碼,在第二個框裏面,發現 setRunningText()
這個方法,並且方法有個參數是:totalYesterdayProfitView
,意思是昨天總收益,那這裏應該就是【昨日收益】設值的地方
那就是說前一個setText()
方法,若是沒有錯誤的話,就是設值【總資產】的地方,設值變量爲 latestTotalView
。
爲了確保沒有錯誤,跟一下變量 a 和變量 b 具體是什麼類型,能夠看到一個是 AUAutoResizeTextView
另外一個 NumRunningTextView
,這應該是自定義的控件,爲啥總資產要定義爲自動調整大小的View呢?想一下若是寫死寬度的話或者字體大小的,就兼容不了每一個人的資產數目,如:1000000 和 1 長度的區別。
AssetsCardModel
應該是存儲用戶資產信息的 Model 類,框框裏面的變量,就是存儲着最新的【總資產】信息
好的,到這裏分析完了,總結一下具體是怎麼個流程:
LauncherActivity -> TabHost -> TabLauncherFragment -> FortureHomeView.updateFortureHead() -> AssetCardV2View.renderData() -> AssetHeaderV2View.setData() -> AUAutoResizeTextView.setText()
如今只須要對 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);
}
});
}
}
}
}
複製代碼
相關閱讀:
更多技術分享,請加微信公衆號——碼農茅草屋: