Android之Xposed框架完全使用指南
文章目錄
- Xposed環境搭建
- Xposed簡介
- Xposed原理
- Xposed的安裝
- Xposed插件開發
- Xposed插件編寫流程
- Xposed開發之Hook構造函數
- 相關API
- 無參構造函數的hook
- 有參構造函數的hook
- 實際效果
- Xposed開發之修改屬性
- 相關API
- 修改靜態字段和成員字段
- 實際效果
- Xposed開發之hook一般函數
- 相關API
- hook一般函數
- 實際效果
- Xposed開發之主動調用函數
- 相關API
- 調用靜態函數
- 調用成員函數
- 實際效果
- Xposed開發之加殼APP處理
- 實際效果
- Xposed指紋檢測
Xposed環境搭建
Xposed簡介
Xposed是一款可以在不修改APK的情況下影響程序運行的框架,基于它可以制作出許多功能強大的模塊,且在功能不沖突的情況下同時運作。在這個框架下,我們可以編寫并加載自己編寫的插件APP,實現對目標apk的注入攔截等。
Xposed原理
控制zygote進程,通過替換/system/bin/app_precess程序控制zygote進程,使得它在系統啟動的時候會加載Xposed framework的一個jar文件即XposedBridge.jar,從而完成對zygote進程及其創建的Dalvik/ART虛擬機的劫持,并且能夠允許開發者獨立的替代任何class。
Xposed的安裝
4.4以下的Android版本安裝較為簡單,只需要兩步:
從Android 5.0開始,谷歌使用ART替換Dalvik,所以Xposed安裝有點麻煩,分為兩個部分:xposed*.zip和XposedInstaller.apk。zip文件是框架主體,需要進入Recovery后刷入,apk文件是xposed模塊管理應用的,主要用于下載,激活,是否啟用模塊等功能管理。步驟如下:
實際操作如下:
http://dl-xda.xposed.info/framework/首先去官網下載對應的補丁包,sdk23對應Android 6.0版本,其他對應版本請自行查詢,我這里用的是6.0的系統
然后選擇對應的系統架構
版本選擇最新版,接著按照上面的步驟安裝xposed模塊即可。
Xposed插件開發
Xposed插件編寫流程
將jar包導入到模塊,并設置Configuration為compileOnly
實現IXposedHookLoadPackage接口,然后在handleLoadPackage函數內編寫Hook代碼
Xposed開發之Hook構造函數
相關API
需要用到的API如下:
XposedHelpers.findAndHookConstructor無參構造函數的hook
首先編寫一個目標hook類,類代碼包含多個構造函數
package com.example.hookdemo01;public class Student {String name;String id;int age;public Student(){name="default";id="default";}public Student(String name) {this.name = name;}public Student(String name, String id) {this.name = name;this.id = id;}public Student(String name, String id, int age) {this.name = name;this.id = id;this.age = age;} }然后打印出類的信息
package com.example.hookdemo01;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.util.Log;public class MainActivity extends AppCompatActivity {public void PrintStudent(Student stu){Log.i("Xposed",stu.name+"--"+stu.id+"--"+stu.age);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Student studenta=new Student();Student studentb=new Student("GuiShou");Student studentc=new Student("GuiShou2","2021");Student studentd=new Student("GuiShou3","2021",20);PrintStudent(studenta);PrintStudent(studentb);PrintStudent(studentc);PrintStudent(studentd);} }接著編寫hook代碼,代碼實現寫在handleLoadPackage里
//判斷包名是否是要hook的包if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//獲取當前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//獲取當前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hook無參構造函數 參數列表為空 第二個參數類型不需要傳入XposedHelpers.findAndHookConstructor(studentClass, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student() is called! beforeHookedMethod");}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student() is called! afterHookedMethod");}});有參構造函數的hook
然后編寫有參函數的hook代碼
//hook一個參數的構造函數 傳入參數類型XposedHelpers.findAndHookConstructor(studentClass,String.class ,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//獲取參數數組Object[] argsarray=param.args;String name=(String)argsarray[0];//打印參數名XposedBridge.log("com.example.hookdemo01.Student(String) is called! beforeHookedMethod--"+name);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String) is called! afterHookedMethod");}});//hook兩個參數的構造函數 傳入參數類型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//獲取參數數組Object[] argsarray=param.args;String name=(String)argsarray[0];String id=(String)argsarray[1];//打印參數名XposedBridge.log("com.example.hookdemo01.Student(String,String) is called! beforeHookedMethod--"+name+"--"+id);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String) is called! afterHookedMethod");}});//hook三個參數的構造函數 傳入參數類型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,int.class,new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//獲取參數數組Object[] argsarray=param.args;String name=(String)argsarray[0];String id=(String)argsarray[1];int age=(int)argsarray[2];//打印參數名XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! beforeHookedMethod--"+name+"--"+id+"--"+age);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! afterHookedMethod");}});這里只需要調用findAndHookConstructor函數,完成回調函數編寫即可
實際效果
啟用xposed模塊,重啟目標app
可以看到這里打印出了所有相關的信息,如果需要還可以對參數進行修改
Xposed開發之修改屬性
相關API
需要用到的API如下:
XposedHelpers.getStaticObjectField XposedHelpers.setStaticObjectField XposedHelpers.getObjectField XposedHelpers.setObjectFieldXposed不只是可以實現對app自己實現的類構造函數的hook,對于類的屬性字段也提供了一系列修改的API,首先修改目標類代碼
package com.example.hookdemo01; import android.util.Log;public class Student {String name;String id;int age;private String nickname;public static String teachername;public Student(String name, String id, int age, String teacher,String nickname) {this.name = name;this.id = id;this.age = age;this.nickname = nickname;teachername=teacher;Log.i("Xposed","構造函數--teachername:"+teachername);Log.i("Xposed","構造函數--nickname:"+nickname);} }接著編寫hook代碼
修改靜態字段和成員字段
package com.example.xposeddemo01; import java.lang.reflect.Field; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage;public class Xposed01 implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {//判斷包名是否是要hook的包if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//獲取當前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//獲取當前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hook三個參數的構造函數 傳入參數類型XposedHelpers.findAndHookConstructor(studentClass,String.class ,String.class,int.class,String.class,String.class,new XC_MethodHook() {@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.example.hookdemo01.Student(String,String,int) is called! afterHookedMethod");//------------------------修改static屬性-------------------------------------//設置teacher字段XposedHelpers.setStaticObjectField(studentClass,"teachername","teacher888");//獲取teacher字段 打印輸出String teacher = (String)XposedHelpers.getStaticObjectField(studentClass,"teachername");XposedBridge.log("修改后的teachername字段:"+teacher);//-------------------修改對象屬性-----------------------------------------XposedHelpers.setObjectField(param.thisObject,"nickname","pandan888");//獲取nickname字段 打印輸出String nickname=(String)XposedHelpers.getObjectField(param.thisObject,"nickname");XposedBridge.log("修改后的nickname字段:"+nickname);}});}} }對于修改靜態字段和成員字段,需要使用不同的API,xposed類內部已經取消了字段的訪問檢查,所以不需要自己取消檢查,比反射修改字段的方案更加簡潔
實際效果
Xposed開發之hook一般函數
相關API
XposedHelpers.findAndHookMethodhook一般函數
首先修改一下目標app的代碼
public static String publicstaticfun(String arg1,int arg2){//String result= privatestaticfun("test2",200);return arg1+"---"+arg2;}增加一個靜態的成員函數,接著編寫hook代碼
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {if (loadPackageParam.packageName.equals("com.example.hookdemo01")){//獲取當前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//獲取當前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//hookXposedHelpers.findAndHookMethod(studentClass, "publicstaticfun", String.class, int.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);//打印參數Object[] objectarray=param.args;String arg0=(String)objectarray[0];int arg1=(int)objectarray[1];XposedBridge.log("beforeHookedMethod---arg0:"+objectarray[0]+"arg1:"+objectarray[1]);//修改參數objectarray[0]="GuiShou";objectarray[1]=888;XposedBridge.log("beforeHookedMethod---arg0:"+objectarray[0]+"arg1:"+objectarray[1]);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);//打印返回值String result=(String)param.getResult();XposedBridge.log("afterHookedMethod---result:"+result);//修改返回值param.setResult("this it result");String result2=(String)param.getResult();XposedBridge.log("afterHookedMethod---result:"+result2);}});}}實際效果
可以看到,這里已經打印出了修改前后的參數和返回值。實際上,類中的其他函數,例如私有成員函數,匿名內部類函數等等,都是用同樣的方法進行hook,只要填入jadx反編譯的類名和函數名即可。
Xposed開發之主動調用函數
public static String publicstaticfun(String arg1,int arg2){Log.i("xposed","publicstaticfun is call");return arg1+"---"+arg2;}private String privatefun(String arg1,int arg2){Log.i("xposed","privatefun is call");return arg1+"---"+arg2;}首先修改代碼,增加一個靜態函數和一個成員函數,調用時觸發log信息
相關API
XposedHelpers.callStaticMethod() XposedHelpers.callMethod()調用靜態函數
//獲取當前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//獲取當前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//調用靜態函數XposedHelpers.callStaticMethod(studentClass,"publicstaticfun","guishou",100);調用成員函數
//獲取當前的classloaderClassLoader classLoader=loadPackageParam.classLoader;//獲取當前的ClassClass studentClass=classLoader.loadClass("com.example.hookdemo01.Student");//調用構造函數 獲取對象Object obj = XposedHelpers.newInstance(studentClass,"GuiShou3","2021",20,"teacher666","pandan666");//調用成員函數XposedHelpers.callMethod(obj,"privatefun","guishou",100);實際效果
可以看到,靜態函數和成員函數均被調用了。對于類中的靜態函數,直接調用即可;對于成員函數,需要先得到類的實例,然后才能完成調用。
Xposed開發之加殼APP處理
對于加殼app的hook處理,實際上就是解決ClassLoader的問題。
對于加殼的APP,如果我們直接去hook當前的apk的話,那么殼代碼中的ClassLoder必定是沒有我們所需要hook的目標類。
由于殼代碼的ClassLoader中并沒有我們需要的類,所以在編寫hook代碼之前還需要一個步驟,就是在殼代碼的修正ClassLoader之后,利用反射的方式拿到修正后的ClassLoader,然后傳入修正后的ClassLoader,再對目標類進行hook
實際代碼如下:
public static Field getClassField(ClassLoader classloader, String class_name,String filedName) {try {Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);return field;} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj,String filedName) {try {Class obj_class = classloader.loadClass(class_name);//Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);Object result = null;result = field.get(obj);return result;} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}public static Object invokeStaticMethod(String class_name,String method_name, Class[] pareTyple, Object[] pareVaules) {try {Class obj_class = Class.forName(class_name);Method method = obj_class.getMethod(method_name, pareTyple);return method.invoke(null, pareVaules);} catch (SecurityException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}public static Object getFieldOjbect(String class_name, Object obj,String filedName) {try {Class obj_class = Class.forName(class_name);Field field = obj_class.getDeclaredField(filedName);field.setAccessible(true);return field.get(obj);} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NullPointerException e) {e.printStackTrace();}return null;}public static ClassLoader getClassloader() {ClassLoader resultClassloader = null;Object currentActivityThread = invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[]{}, new Object[]{});Object mBoundApplication = getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mBoundApplication");Application mInitialApplication = (Application) getFieldOjbect("android.app.ActivityThread",currentActivityThread, "mInitialApplication");Object loadedApkInfo = getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "info");Application mApplication = (Application) getFieldOjbect("android.app.LoadedApk", loadedApkInfo, "mApplication");resultClassloader = mApplication.getClassLoader();return resultClassloader;}調用代碼中的getClassloader函數,即可獲取到修正后的classloader,接著編寫hook代碼
@Overridepublic void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {if (loadPackageParam.packageName.equals("com.kanxue.xposedhook01")) {//對殼入口類的onCreate函數進行hook 拿到classloaderXposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() {@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("com.stub.StubApp->onCreate afterHookedMethod");ClassLoader finalClassLoader=getClassloader();XposedBridge.log("finalClassLoader->" + finalClassLoader);GetClassLoaderClasslist(finalClassLoader);//再對目標類進行hook 傳入拿到的classloaderXposedHelpers.findAndHookMethod("com.kanxue.xposedhook01.Student", finalClassLoader, "privatefunc", String.class, int.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);Object[] objectarray = param.args;String arg0 = (String) objectarray[0];int arg1 = (int) objectarray[1];XposedBridge.log("beforeHookedMethod11 privatefunc->arg0:" + arg0 + "---arg1:" + arg1);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);String result = (String) param.getResult();XposedBridge.log("afterHookedMethod11 privatefunc->result:" + result);}});Class personClass = XposedHelpers.findClass("com.kanxue.xposedhook01.Student$person", finalClassLoader);XposedHelpers.findAndHookMethod(personClass, "getpersonname", String.class, new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {super.beforeHookedMethod(param);XposedBridge.log("beforeHookedMethod getpersonname->" + param.args[0]);}@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {super.afterHookedMethod(param);XposedBridge.log("afterHookedMethod getpersonname->" + param.getResult());}});}});}}首先需要hook入口類中的onCreate函數,在onCreate函數執行完成之后,獲取到修正后的ClassLoader,接著再對目標類進行hook,此時hook的代碼和hook一般函數一樣,區別在于需要傳入修正后的ClassLoder
實際效果
實際效果如圖:可以看到這里已經成功對目標函數進行hook,打印出了hook之前和之后的函數相關信息。
Xposed指紋檢測
可以根據下面的特征對xposed框架進行檢測
github上檢測Xposed的demo:XposedChecker
總結
以上是生活随笔為你收集整理的Android之Xposed框架完全使用指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VMP分析之VMP2.13插件化分析(四
- 下一篇: 最强黑客库Blackbone使用教程