
一、前言
Xposed 能干嘛?我可以告訴你 Root + Xposed ,真的可以為所欲為。而 Android 開源,為“搞機”帶了更多的樂趣的同時,當(dāng)然也引入安全性問題,部分流氓軟件在 Root 下,會盜取用戶私密信息,例如:號碼、照片、短信、密碼······,所以需要慎重使用 Root,此外本文僅作為技術(shù)學(xué)習(xí)
二、Xposed 安裝
1.首先你的手機 必須 Root,關(guān)于各安卓機型的 Root 方法,請自行到對應(yīng)機型論壇和貼吧找找 (注:Root 有風(fēng)險,失敗可能導(dǎo)致手機變磚,風(fēng)險自行承擔(dān))
2.下載「Xposed Installer」軟件并安裝,需要留意的是你的手機系統(tǒng)版本,不同版本下載對應(yīng)的 apk,如果上不了該網(wǎng)址,可百度搜索——Xposed Installer 最新版本下載。
- Xposed 官方網(wǎng)址:
https://repo.xposed.info/module/de.robv.android.xposed.installer
- Android 5.0 及以上版本的下載地址:

3.激活 Xposed 框架(確保你手機刷入了第三方 Recovery),激活后可能會使系統(tǒng)變的有些卡頓,但為了技術(shù)(裝B),我們犧牲點性能還是值得~
- 點擊【安裝/更新】,選擇【Install via recovery】
- 等待【下載】,重啟到【Recovery】模式,期間請勿操作
- 耐心等待,刷完會自動重啟,打開【Xposed Installer】,顯示框架已激活。


三、配置 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 插件,設(shè)置為 true
- xposeddescription:模塊名稱
- xposedminversion:最低版本號
3、新建 Hook 入口類
該類需要實現(xiàn)接口 IXposedHookLoadPackage,并實現(xiàn)里面關(guān)鍵方法handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam),該方法會在每個軟件被啟動的時候回調(diào),所以一般需要通過目標(biāo)包名過濾。
/**
* @author zhicheng.chen
*/
public class TargetHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
//通過目標(biāo)包名過濾
if (lpparam.packageName.equals("com.xxx.xxx")) {
XposedBridge.log("啟動了xxx軟件");
}
}
}
4、Xposed 免重啟調(diào)試
Xposed插件每次代碼改動,都需要重啟手機才能生效,有時候重啟一次還不生效(我的手機有一次重啟 3 次,才看到生效,還好是公司測試機,不心疼),所以代碼最好寫上相關(guān) Log 信息,來看代碼生效沒。
XposedBridge.log("啟動了xxx軟件");
不過這里分享一個免重啟調(diào)試的方法,方法來自網(wǎng)上,感謝 DX :
/**
* @author DX
* 這種方案建議只在開發(fā)調(diào)試的時候使用,因為這將損耗一些性能(需要額外加載apk文件),調(diào)試沒問題后,直接修改xposed_init文件為正確的類即可
* 可以實現(xiàn)免重啟,由于存在緩存,需要殺死宿主程序以后才能生效
* 這種免重啟的方式針對某些特殊情況的hook無效
* 例如我們需要implements IXposedHookZygoteInit,并將自己的一個服務(wù)注冊為系統(tǒng)服務(wù),這種就必須重啟了
* Created by DX on 2017/10/4.
*/
public class HookLoader2 implements IXposedHookLoadPackage {
//按照實際使用情況修改下面幾項的值
/**
* 當(dāng)前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以后,系統(tǒng)會在/data/app/下備份了一份.apk文件,通過動態(tài)加載這個apk文件,調(diào)用相應(yīng)的方法
* 這樣就可以實現(xiàn),只需要第一次重啟,以后修改hook代碼就不用重啟了
* @param context context參數(shù)
* @param modulePackageName 當(dāng)前模塊的packageName
* @param handleHookClass 指定由哪一個類處理相關(guān)的hook邏輯
* @param loadPackageParam 傳入XC_LoadPackage.LoadPackageParam參數(shù)
* @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邏輯處理類,并調(diào)用它的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);
}
/**
* 根據(jù)包名構(gòu)建目標(biāo)Context,并調(diào)用getPackageCodePath()來定位apk
* @param context context參數(shù)
* @param modulePackageName 當(dāng)前模塊包名
* @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
安裝這個工具可以直接查看當(dāng)前 App 顯示的最前 Activity,后面我們接觸別人寫的項目,這個工具可以很方便跟蹤代碼的入口,項目原理是通過 AccessibilityService 服務(wù),監(jiān)聽所有的界面變化,讀取當(dāng)前界面 Activity,需要給軟件開啟【輔助權(quán)限】。
當(dāng)然也可以通過 Adb 命令,獲取 Dumpsys 當(dāng)前 Activity 的信息:
adb shell dumpsys activity top

2、BDOpener.apk
這是 Xposed 的插件模塊,也就是說需要刷入 Xposed 框架后,才能使用該,通過安裝這個軟件,我們可以使手機安裝的軟件,變?yōu)?strong>可調(diào)試狀態(tài),這樣就可以通過 Android Studio 的 Monitor 工具,查看所有的進程狀態(tài),并 dumpsys 方法調(diào)用信息。
安裝這個軟件之后,需要在 Xposed 里面的【模塊】里面,把該軟件勾選后,【重啟手機】才能生效。
有人說找不到 Monitor 工具,該工具在新的 As 版本把入口取消了,我們可以在:C:\Users\Administrator\AppData\Local\Android\Sdk\tools\monitor.bat 目錄下找到。
開啟調(diào)試狀態(tài)之前,只能看到 AS 調(diào)試安裝的進程:

開啟調(diào)試狀態(tài)之后,可以看到所有的進程信息:

3、jadx-gui 工具
關(guān)于這個工具的介紹,這里我就不再贅述,貼一篇我覺得寫得很好的博文,作者講得很詳細(xì)易懂。
Android 反編譯利器,jadx 的高級技巧:http://www.itdecent.cn/p/e5b021df2170

注:如果出現(xiàn) Jdk 錯誤,請安裝 Jdk 64 位版本。
相關(guān)閱讀: