配置 Xposed 模块的 AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="your.package.name"><applicationandroid:label="Your Xposed Module Name"android:icon="@mipmap/ic_launcher"><meta-dataandroid:name="xposedmodule"android:value="true" /><meta-dataandroid:name="xposeddescription"android:value="这是一个用于监控字符串操作的 Xposed 模块" /><meta-dataandroid:name="xposedminversion"android:value="53" /><meta-dataandroid:name="xposedscope"android:resource="@array/xposed_scope" /></application>
</manifest>
在app/src/main/assets创建一个xposed_init文件 。
xposed_init 文件是 Xposed 模块必需的一个配置文件,它用来指定模块的入口类。这个文件需要包含你的 Xposed 模块的主类的完整类名(包含包名):
your.package.name.MainHook
app/build.gradle配置一下:
dependencies {implementation 'androidx.appcompat:appcompat:1.6.1'implementation 'com.google.android.material:material:1.9.0'// Xposed Framework APIcompileOnly 'de.robv.android.xposed:api:82'compileOnly 'de.robv.android.xposed:api:82:sources'// 如果需要使用 LSPosed API(可选)// compileOnly 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
}
LoadPackageParam (简称 lpparam) 是 Xposed 框架中的一个重要参数类,它包含了被加载的应用程序的相关信息。
LoadPackageParam 是 XC_LoadPackage 的一个内部类,以下是它的所有成员变量:
public static final class LoadPackageParam extends XCallback.Param {/** 应用的包名 */public String packageName;/** 进程的名称 */public String processName;/** 应用的 ClassLoader */public ClassLoader classLoader;/** 应用的 Application 对象 */public ApplicationInfo appInfo;/** 是否是第一次加载 */public boolean isFirstApplication;/** 系统服务的进程名(如果是系统服务) */public String[] initiatingPackages;/** 系统服务的进程名(如果是系统服务) */public String initiatingPackage;
}
// 获取当前加载的应用包名String pkgName = lpparam.packageName;// 例如: "com.android.chrome"
// 获取当前进程名String procName = lpparam.processName;// 可能是: "com.android.chrome"// 或者: "com.android.chrome:sandbox"
类加载器
- 它负责找到并加载你需要的工具(类)
// 相当于说"帮我找到这个工具"
Class<?> targetClass = lpparam.classLoader.loadClass("com.example.Target");
// 找到后就可以使用这个类了
XposedHelpers.findAndHookMethod(targetClass, "方法名", ...);
// 获取应用的类加载器ClassLoader loader = lpparam.classLoader;// 用于加载目标应用中的类Class<?> targetClass = loader.loadClass("com.example.Target");
// 获取应用的信息ApplicationInfo info = lpparam.appInfo;// 可以获取很多应用相关信息String sourceDir = info.sourceDir; // APK 路径String nativeLibDir = info.nativeLibraryDir; // native库路径int targetSdkVersion = info.targetSdkVersion; // 目标SDK版本
XposedHelpers
查找和 Hook 方法
// 1. 基本的方法 Hook
XposedHelpers.findAndHookMethod("com.example.Class", // 类名lpparam.classLoader, // 类加载器"methodName", // 方法名String.class, // 参数类型1int.class, // 参数类型2new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) {// 方法执行前的处理}@Overrideprotected void afterHookedMethod(MethodHookParam param) {// 方法执行后的处理}}
);// 2. Hook 构造方法
XposedHelpers.findAndHookConstructor(targetClass, // 类String.class, // 参数类型new XC_MethodHook() { ... }
);
获取/设置字段值
// 获取字段值
Object fieldValue = XposedHelpers.getObjectField(object, "fieldName");
int intValue = XposedHelpers.getIntField(object, "fieldName");
String stringValue = XposedHelpers.getStaticObjectField(class, "fieldName");// 设置字段值
XposedHelpers.setObjectField(object, "fieldName", newValue);
XposedHelpers.setIntField(object, "fieldName", 123);
XposedHelpers.setStaticObjectField(class, "fieldName", newValue);
调用方法
// 调用实例方法
Object result = XposedHelpers.callMethod(object, "methodName", arg1, arg2);// 调用静态方法
Object result = XposedHelpers.callStaticMethod(class, "methodName", arg1, arg2);
创建新实例
// 创建对象实例
Object newInstance = XposedHelpers.newInstance(class);
Object newInstance = XposedHelpers.newInstance(class, "构造参数1", "构造参数2");
查找类
// 查找类
Class<?> class = XposedHelpers.findClass("com.example.Class", lpparam.classLoader);
Hook Api
GravityBox
GravityBox 是一个非常著名的 Xposed 模块,它是一个系统级的调整工具箱。
-
更改赋予 Android 终端硬件按钮的功能
-
修改状态栏的外观和显示的选项
-
改变可以在手机或平板电脑屏幕上显示的亮度(这个是积极的,尤其是最小的,以节省电池)
-
直接应用程序分配给设备的触摸按钮
-
管理终端 RAM 的使用,了解此应用程序的消耗
Xposed Hook
- choose: 查找某个类的所有实例对象
- enumerateClassLoaders: 查找所有的类加载器
Java.choose() 和 Java.enumerateClassLoaders() 是 Frida 中两个不同的 API,它们的用途不同:
// 用于查找指定类的所有实例
Java.choose("com.example.TargetClass", {onMatch: function(instance) {// 每找到一个实例就会调用一次console.log("找到实例:", instance);console.log("实例字段值:", instance.fieldName);},onComplete: function() {// 搜索完成时调用console.log("搜索完成");}
});
// 用于列举所有的类加载器
Java.enumerateClassLoaders({onMatch: function(loader) {// 每找到一个类加载器就会调用一次console.log("类加载器:", loader);// 可以尝试用这个加载器加载类try {loader.loadClass("com.example.TargetClass");console.log("这个加载器可以加载目标类");} catch(e) {console.log("这个加载器无法加载目标类");}},onComplete: function() {console.log("搜索完成");}
});
主要区别:
- choose: 查找某个类的所有实例对象
- enumerateClassLoaders: 查找所有的类加载器
简单说:
- 想找对象用 choose
- 想找类加载器用 enumerateClassLoaders
// 1. 最简单的使用方式
Java.perform(function() {// 获取默认的类加载器var targetClass = Java.use("com.example.TargetClass");
});// 2. 当默认加载器找不到类时,可以遍历所有"图书管理员"
Java.enumerateClassLoaders({onMatch: function(loader) {try {// 让每个"管理员"都尝试找这本"书"loader.loadClass("com.example.TargetClass");console.log("找到了!这个管理员可以找到这本书");} catch(e) {console.log("这个管理员找不到这本书");}}
});
让我用更简单的方式解释 IXposedHookLoadPackage:
想象你是一个保安,站在商场门口:
- 每当有人(应用)要进商场时,你都会被通知
- 你可以检查他们的身份(包名),决定要不要对他们做什么
// 你就是这个保安
public class MainHook implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {// 1. 检查是不是你要找的人if (lpparam.packageName.equals("com.taobao.qianniu")) {// 是千牛应用XposedBridge.log("发现千牛启动了!");// 2. 对这个应用做一些事// 比如:监控它的某个方法XposedHelpers.findAndHookMethod("com.taobao.qianniu.MainActivity", // 类名lpparam.classLoader, // 类加载器"onCreate", // 方法名Bundle.class, // 参数类型new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) {XposedBridge.log("千牛正在启动...");}});}}
}
handleLoadPackage 是 IXposedHookLoadPackage 接口中唯一需要实现的方法。它的作用是:
public class MainHook implements IXposedHookLoadPackage {@Overridepublic void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {// 这个方法会在每个应用启动时被调用// 1. 可以获取应用的包名String packageName = lpparam.packageName;// 2. 可以获取应用的类加载器ClassLoader classLoader = lpparam.classLoader;// 3. 实际使用示例if (packageName.equals("com.taobao.qianniu")) {// 找到目标应用后,就可以开始 Hook 了XposedBridge.log("找到千牛了!");// Hook 示例XposedHelpers.findAndHookMethod("目标类名",classLoader,"方法名",new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) {// 方法执行前的处理}});}}
}
// Xposed 模块中使用 XposedBridge.log
XposedBridge.log("发现目标应用:" + TARGET_PACKAGE); // 这是 Java 代码
// Frida 脚本中使用 console.log
console.log("发现目标应用:" + TARGET_PACKAGE); // 这是 JavaScript 代码
XC_MethodHook 是 Xposed 框架中用来 Hook 方法的核心类。它让你可以在方法执行前后添加自己的代码。
XposedHelpers 最常用的方法
// 1. Hook 相关
XposedHelpers.findAndHookMethod( // Hook 实例方法className, // 类名classLoader, // 类加载器methodName, // 方法名parameterTypes, // 参数类型callback // 回调
);XposedHelpers.findAndHookConstructor( // Hook 构造方法className,classLoader,parameterTypes,callback
);// 2. 查找类
Class<?> cls = XposedHelpers.findClass("com.example.TargetClass",classLoader
);// 3. 调用方法
// 调用实例方法
Object result = XposedHelpers.callMethod(object, // 对象"methodName", // 方法名params // 参数
);// 调用静态方法
Object result = XposedHelpers.callStaticMethod(className, // 类名"methodName", // 方法名params // 参数
);// 4. 获取/设置字段
// 获取实例字段
Object value = XposedHelpers.getObjectField(object, // 对象"fieldName" // 字段名
);// 获取静态字段
Object value = XposedHelpers.getStaticObjectField(className, // 类名"fieldName" // 字段名
);// 设置实例字段
XposedHelpers.setObjectField(object, // 对象"fieldName", // 字段名newValue // 新值
);// 设置静态字段
XposedHelpers.setStaticObjectField(className, // 类名"fieldName", // 字段名newValue // 新值
);// 5. 创建新实例
Object newObj = XposedHelpers.newInstance(className, // 类名params // 构造参数
);
setAccessible 是 Java 反射中用来绕过访问限制的方法。它可以让你访问私有成员:
// 1. 基本用法
Field field = targetClass.getDeclaredField("privateField");
field.setAccessible(true); // 允许访问私有字段
Object value = field.get(object); // 现在可以访问了// 2. 实际例子
try {// 获取私有方法Method method = targetClass.getDeclaredMethod("privateMethod");method.setAccessible(true); // 设置可访问method.invoke(object); // 调用私有方法// 获取私有字段Field field = targetClass.getDeclaredField("privateField");field.setAccessible(true); // 设置可访问field.set(object, newValue); // 修改私有字段值
} catch (Exception e) {XposedBridge.log("访问失败: " + e);
}
- Java.use().$new(): 创建新的 Java 对象
- Java.cast(): 转换对象类型
- Java.choose(): 查找已存在的对象实例
$new() 里面的参数对应 Java 类的构造函数参数。让我用具体例子说明:
Java.perform(function() {// 1. 无参数构造函数var StringBuilder = Java.use("java.lang.StringBuilder");var sb1 = StringBuilder.$new(); // 等同于 new StringBuilder()// 2. 带参数构造函数var sb2 = StringBuilder.$new("Hello"); // 等同于 new StringBuilder("Hello")// 3. String 类例子var String = Java.use("java.lang.String");var str1 = String.$new(); // new String()var str2 = String.$new("Hello"); // new String("Hello")// 4. 自定义类例子var MyClass = Java.use("com.example.MyClass");// 如果 MyClass 构造函数需要两个参数:String 和 intvar myObj = MyClass.$new("参数1", 123); // new MyClass("参数1", 123)
});
下面是Java.cast(): 转换对象类型:
- View 就像是一个"容器"
- 通过 cast 告诉系统:"这个容器其实是个按钮"
- 转换后就可以用按钮特有的功能了
Java.perform(function() {// 1. 找到一个普通的 ViewJava.choose("com.example.MainActivity", {onMatch: function(activity) {// findViewById 返回的是 View 类型var view = activity.findViewById(123); // 这时候只能用 View 的方法// 把 View 转成 Buttonvar button = Java.cast(view, Java.use("android.widget.Button"));// 现在可以用 Button 特有的方法了button.setText("点击我"); // 设置按钮文字button.setEnabled(true); // 设置按钮可点击button.setOnClickListener(/* ... */); // 设置点击事件}});
});
// TextView 转换
var textView = Java.cast(view, Java.use("android.widget.TextView"));
textView.setText("这是文本");// ImageView 转换
var imageView = Java.cast(view, Java.use("android.widget.ImageView"));
imageView.setImageResource(R.drawable.icon);// EditText 转换
var editText = Java.cast(view, Java.use("android.widget.EditText"));
editText.setHint("请输入...");
主要区别:
- $new() 是 Frida (JavaScript) 的方法
- newInstance 是 Xposed (Java) 的方法
- 功能是一样的,都是创建新对象
简单说:
- 在 Frida 脚本中用 $new()
- 在 Xposed 模块中用 newInstance
Frida 的 $new()
Java.perform(function() {// Frida 方式创建对象var String = Java.use("java.lang.String");var str = String.$new("Hello"); // 创建字符串var ArrayList = Java.use("java.util.ArrayList");var list = ArrayList.$new(); // 创建列表
});
Xposed 的 newInstance
// Xposed 方式创建对象
String str = (String) XposedHelpers.newInstance(String.class, // 类"Hello" // 参数
);ArrayList list = (ArrayList) XposedHelpers.newInstance(ArrayList.class // 类
);
简单说就是三步:
- 找到类 (findClass)
- 创建实例 (newInstance)
- 调用方法 (callMethod)
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {// 1. 首先查找类Class<?> targetClass = XposedHelpers.findClass("com.example.TargetClass", // 类名lpparam.classLoader // 类加载器);// 2. 创建类的实例Object instance = XposedHelpers.newInstance(targetClass);// 如果构造函数有参数// Object instance = XposedHelpers.newInstance(targetClass, "参数1", 123);// 3. 调用实例的方法XposedHelpers.callMethod(instance, // 实例对象"methodName", // 方法名"参数1", // 方法参数123 // 更多参数);
}
NanoHTTPD
NanoHTTPD 是一个轻量级的 HTTP 服务器库。