Xposed模块编写学习
Xposed模块编写学习
Xposed原理
Xposed是一款可以在不修改APK的情况下影响程序运行的框架,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。在这个框架下,我们可以编写并加载自己编写的插件APP,实现对目标apk的注入拦截等。
用自己实现的app_process替换掉了系统原本提供的app_process,加载一个额外的jar包,入口从原来的: **com.android.internal.osZygoteInit.main()被替换成了: de.robv.android.xposed.XposedBridge.main()**,
创建的Zygote进程就变成Hook的Zygote进程了,从而完成对zygote进程及其创建的Dalvik/ART虚拟机的劫持(zytoge注入)
从本质上来讲,Xposed 模块也是一个 Android 程序。但与普通程序不同的是,想要让写出的Android程序成为一个Xposed 模块,要额外多完成以下四个硬性任务:
1、让手机上的xposed框架知道我们安装的这个程序是个xposed模块。
2、模块里要包含有xposed的API的jar包,以实现下一步的hook操作。
3、这个模块里面要有对目标程序进行hook操作的方法。
4、要让手机上的xposed框架知道,我们编写的xposed模块中,哪一个方法是实现hook操作的。
这就引出我即将要介绍的四大件(与前四步一一对照):
1、AndroidManifest.xml
2、XposedBridgeApi-xx.jar 与 build.gradle
3、实现hook操作的具体代码
4、xposed_Init
XposedBridgeApi
下载地址:https://github.com/bywhat/XposedBridgeApi 这里有54、82、89版本的
作为项目依赖
项目创建
Android Studio启动!
新建项目
那就选一个No Activity吧。(这个是模块的界面,随意选择即可)
选java
添加依赖
选择Project 并创建一个libs文件夹,然后将api添加为库
修改xml
这里修改AndroidManifest.xml
在<application>
标签下添加下面内容:
<!-- 是否是xposed模块,xposed根据这个来判断是否是模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- 模块描述,显示在xposed模块列表那里第二行 -->
<meta-data
android:name="xposeddescription"
android:value="这是一个Xposed模块" />
<!-- 最低xposed版本号(lib文件名可知) -->
<meta-data
android:name="xposedminversion"
android:value="89" />
修改后:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Xposeddemo"
tools:targetApi="31" >
<!-- 是否是xposed模块,xposed根据这个来判断是否是模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- 模块描述,显示在xposed模块列表那里第二行 -->
<meta-data
android:name="xposeddescription"
android:value="这是一个Xposed模块" />
<!-- 最低xposed版本号(lib文件名可知) -->
<meta-data
android:name="xposedminversion"
android:value="89" />
</application>
</manifest>
修改build.gradle
将此处修改为compileOnly 默认的是implementation
implementation 使用该方式依赖的库将会参与编译和打包
compileOnly 只在编译时有效,不会参与打包
新建assets目录
创建文件夹后在这个文件夹下创建一个名为xposed_init
的文件,这个文件用于声明模块入口
例如:
com.example.xposeddemo.Hook
创建Hook类
实现IXposedHookLoadPackage接口,然后在handleLoadPackage函数内编写Hook逻辑
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class Hook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
}
}
常用的API
beforeHookedMethod
: 这个方法允许你在目标方法执行前对参数进行修改,或者在方法执行前进行一些其他操作。通常用于在目标方法执行前进行一些准备工作,或者改变即将传入目标方法的参数。afterHookedMethod
: 相比之下,这个方法则是在目标方法执行完毕后进行操作。你可以在这里获取到目标方法的返回值,并且可以对返回值进行修改或者进行一些其他操作。这个方法通常用于在目标方法执行后处理返回结果。
Hook普通方法
修改返回值
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", classLoader, "getPublicInt", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
param.setResult(999);
}
});
修改参数
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", classLoader, "setPublicInt", int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
param.args[0] = 999;
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
Hook复杂&自定义参数
Class a = loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo");
XposedBridge.hookAllMethods(a, "Inner", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
});
Hook替换函数
Class a = classLoader.loadClass("类名")
XposedBridge.hookAllMethods(a,"getId",new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return "";
}
});
Hook加固通杀
使用android.app.Application的attach方法获取classloader
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader(); //获取classloader
...
}
});
Hook变量
静态变量与实例变量:
- 静态变量(static):类被初始化,同步进行初始化
- 非静态变量:类被实例化(产生一个对象的时候),进行初始化
静态变量
final Class clazz = XposedHelpers.findClass("类名", classLoader);
XposedHelpers.setStaticIntField(clazz, "变量名", 999);
实例变量
final Class clazz = XposedHelpers.findClass("类名", classLoader);
XposedBridge.hookAllConstructors(clazz, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//param.thisObject获取当前所属的对象
Object ob = param.thisObject;
XposedHelpers.setIntField(ob,"变量名",9999);
}
});
Hook构造函数
无参构造函数
XposedHelpers.findAndHookConstructor("com.zj.wuaipojie.Demo", classLoader, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
有参构造函数
XposedHelpers.findAndHookConstructor("com.zj.wuaipojie.Demo", classLoader, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
Hook multiDex方法
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ClassLoader cl= ((Context)param.args[0]).getClassLoader();
Class<?> hookclass=null;
try {
hookclass=cl.loadClass("类名");
}catch (Exception e){
Log.e("zj2595","未找到类",e);
return;
}
XposedHelpers.findAndHookMethod(hookclass, "方法名", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
}
});
}
});
主动调用
静态方法:
Class clazz = XposedHelpers.findClass("类名",lpparam.classLoader);
XposedHelpers.callStaticMethod(clazz,"方法名",参数(非必须));
实例方法:
Class clazz = XposedHelpers.findClass("类名",lpparam.classLoader);
XposedHelpers.callMethod(clazz.newInstance(),"方法名",参数(非必须));
Hook内部类
内部类:类里还有一个类class
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo$InnerClass", lpparam.classLoader, "innerFunc",String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
});
反射大法
Class clazz = XposedHelpers.findClass("com.zj.wuaipojie.Demo", lpparam.classLoader);
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo$InnerClass", lpparam.classLoader, "innerFunc",String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
//第一步找到类
//找到方法,如果是私有方法就要setAccessible设置访问权限
//invoke主动调用或者set修改值(变量)
Class democlass = Class.forName("com.zj.wuaipojie.Demo",false,lpparam.classLoader);
Method demomethod = democlass.getDeclaredMethod("refl");
demomethod.setAccessible(true);
demomethod.invoke(clazz.newInstance());
}
});
遍历所有类下的所有方法
XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Class clazz = (Class) param.getResult();
String clazzName = clazz.getName();
//排除非包名的类
if(clazzName.contains("com.zj.wuaipojie")){
Method[] mds = clazz.getDeclaredMethods();
for(int i =0;i<mds.length;i++){
final Method md = mds[i];
int mod = mds[i].getModifiers();
//去除抽象、native、接口方法
if(!Modifier.isAbstract(mod)
&& !Modifier.isNative(mod)
&&!Modifier.isAbstract(mod)){
XposedBridge.hookMethod(mds[i], new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d("zj2595",md.toString());
}
});
}
}
}
}
});
Xposed妙用
字符串赋值定位:
XposedHelpers.findAndHookMethod("android.widget.TextView", lpparam.classLoader, "setText", CharSequence.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d("zj2595",param.args[0].toString());
if(param.args[0].equals("已过期")){
printStackTrace();
}
}
});
private static void printStackTrace() {
Throwable ex = new Throwable();
StackTraceElement[] stackElements = ex.getStackTrace();
for (int i = 0; i < stackElements.length; i++) {
StackTraceElement element = stackElements[i];
Log.d("zj2595","at " + element.getClassName() + "." + element.getMethodName() + "(" + element.getFileName() + ":" + element.getLineNumber() + ")");
}
}
点击事件监听:
Class clazz = XposedHelpers.findClass("android.view.View", lpparam.classLoader);
XposedBridge.hookAllMethods(clazz, "performClick", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Object listenerInfoObject = XposedHelpers.getObjectField(param.thisObject, "mListenerInfo");
Object mOnClickListenerObject = XposedHelpers.getObjectField(listenerInfoObject, "mOnClickListener");
String callbackType = mOnClickListenerObject.getClass().getName();
Log.d("zj2595",callbackType);
}
});
改写布局:
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.ui.ChallengeSixth", lpparam.classLoader,
"onCreate", Bundle.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
View img = (View)XposedHelpers.callMethod(param.thisObject,
"findViewById", 0x7f0800de);
img.setVisibility(View.GONE);
}
});
大佬文章: https://forum.butian.net/share/2248
例子
hook的目标是这个a方法
//类:com.zj.wuaipojie.Demo
public final String a(String str) {
return "这是一个" + str + "方法";
}
它被调用时传参为普通
Log.d(Tag, a("普通"));
下面是获取传入的参数和修改参数的例子
首先先判断包名,xposed会遍历所有的包,如果不是这个包就return掉:
if (!loadPackageParam.packageName.equals("com.zj.wuaipojie")) { //指定包名
return;
}
Tips: 在jadx中可直接选中方法,右键,复制为xposed片段。也可以自己写
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", classLoader, "a", java.lang.String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
把其中的classLoader
修改为loadPackageParam.classLoader
在方法调用前,使用日志的方式打印出参数值,建议使用Log.e(),因为其显示的日志是红色的,显眼,容易区分,也可以使用XposedBridge.log()
并且修改掉参数
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.e("zj2595",param.args[0].toString());
param.args[0] = "——————Hook——————"; //修改参数值
Log.e("zj2595",param.args[0].toString());
}
编译运行:
第一次运行需要在手机或模拟器操作一下,会有xposed模块激活提示:
启动模块,选择系统框架、选择要Hook的App
然后再重新打开被Hook的app:
可用看到参数已经被修改
修改返回值,将a方法的返回修改掉:
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
param.setResult("AAAAAAAAAAAAAAA");
}
也可以通过param.getResult()获取返回值
其他的就不演示了