Xposed-Hook

配置 Xposed 模块的 AndroidManifest.xml:

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="your.package.name">

    <application
        android:label="Your Xposed Module Name"
        android:icon="@mipmap/ic_launcher">

        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        
        <meta-data
            android:name="xposeddescription"
            android:value="这是一个用于监控字符串操作的 Xposed 模块" />
            
        <meta-data
            android:name="xposedminversion"
            android:value="53" />
            
        <meta-data
            android:name="xposedscope"
            android:resource="@array/xposed_scope" />
            
    </application>
</manifest>

在app/src/main/assets创建一个xposed_init文件 。

xposed_init 文件是 Xposed 模块必需的一个配置文件,它用来指定模块的入口类。这个文件需要包含你的 Xposed 模块的主类的完整类名(包含包名):

XML 复制代码
your.package.name.MainHook

app/build.gradle配置一下:

XML 复制代码
dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    
    // Xposed Framework API
    compileOnly '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 的一个内部类,以下是它的所有成员变量:

java 复制代码
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;
}
java 复制代码
   // 获取当前加载的应用包名
   String pkgName = lpparam.packageName;
   // 例如: "com.android.chrome"
java 复制代码
   // 获取当前进程名
   String procName = lpparam.processName;
   // 可能是: "com.android.chrome"
   // 或者: "com.android.chrome:sandbox"

类加载器

  • 它负责找到并加载你需要的工具(类)

// 相当于说"帮我找到这个工具"

Class<?> targetClass = lpparam.classLoader.loadClass("com.example.Target");

// 找到后就可以使用这个类了

XposedHelpers.findAndHookMethod(targetClass, "方法名", ...);

java 复制代码
   // 获取应用的类加载器
   ClassLoader loader = lpparam.classLoader;
   // 用于加载目标应用中的类
   Class<?> targetClass = loader.loadClass("com.example.Target");
java 复制代码
   // 获取应用的信息
   ApplicationInfo info = lpparam.appInfo;
   // 可以获取很多应用相关信息
   String sourceDir = info.sourceDir;        // APK 路径
   String nativeLibDir = info.nativeLibraryDir;  // native库路径
   int targetSdkVersion = info.targetSdkVersion; // 目标SDK版本

XposedHelpers

查找和 Hook 方法

java 复制代码
// 1. 基本的方法 Hook
XposedHelpers.findAndHookMethod(
    "com.example.Class",  // 类名
    lpparam.classLoader,  // 类加载器
    "methodName",        // 方法名
    String.class,        // 参数类型1
    int.class,           // 参数类型2
    new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) {
            // 方法执行前的处理
        }
        
        @Override
        protected void afterHookedMethod(MethodHookParam param) {
            // 方法执行后的处理
        }
    }
);

// 2. Hook 构造方法
XposedHelpers.findAndHookConstructor(
    targetClass,         // 类
    String.class,        // 参数类型
    new XC_MethodHook() { ... }
);

获取/设置字段值

java 复制代码
// 获取字段值
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);

调用方法

java 复制代码
// 调用实例方法
Object result = XposedHelpers.callMethod(object, "methodName", arg1, arg2);

// 调用静态方法
Object result = XposedHelpers.callStaticMethod(class, "methodName", arg1, arg2);

创建新实例

java 复制代码
// 创建对象实例
Object newInstance = XposedHelpers.newInstance(class);
Object newInstance = XposedHelpers.newInstance(class, "构造参数1", "构造参数2");

查找类

java 复制代码
// 查找类
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,它们的用途不同:

javascript 复制代码
// 用于查找指定类的所有实例
Java.choose("com.example.TargetClass", {
    onMatch: function(instance) {
        // 每找到一个实例就会调用一次
        console.log("找到实例:", instance);
        console.log("实例字段值:", instance.fieldName);
    },
    onComplete: function() {
        // 搜索完成时调用
        console.log("搜索完成");
    }
});
javascript 复制代码
// 用于列举所有的类加载器
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
javascript 复制代码
// 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:

想象你是一个保安,站在商场门口:

  • 每当有人(应用)要进商场时,你都会被通知
  • 你可以检查他们的身份(包名),决定要不要对他们做什么
javascript 复制代码
// 你就是这个保安
public class MainHook implements IXposedHookLoadPackage {
    @Override
    public 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() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        XposedBridge.log("千牛正在启动...");
                    }
                }
            );
        }
    }
}

handleLoadPackage 是 IXposedHookLoadPackage 接口中唯一需要实现的方法。它的作用是:

javascript 复制代码
public class MainHook implements IXposedHookLoadPackage {
    @Override
    public 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() {
                    @Override
                    protected 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 最常用的方法


java 复制代码
// 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 反射中用来绕过访问限制的方法。它可以让你访问私有成员:

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 类的构造函数参数。让我用具体例子说明:

javascript 复制代码
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 和 int
    var myObj = MyClass.$new("参数1", 123);  // new MyClass("参数1", 123)
});

下面是Java.cast(): 转换对象类型:

  • View 就像是一个"容器"
  • 通过 cast 告诉系统:"这个容器其实是个按钮"
  • 转换后就可以用按钮特有的功能了
javascript 复制代码
Java.perform(function() {
    // 1. 找到一个普通的 View
    Java.choose("com.example.MainActivity", {
        onMatch: function(activity) {
            // findViewById 返回的是 View 类型
            var view = activity.findViewById(123);  // 这时候只能用 View 的方法
            
            // 把 View 转成 Button
            var button = Java.cast(view, Java.use("android.widget.Button"));
            
            // 现在可以用 Button 特有的方法了
            button.setText("点击我");     // 设置按钮文字
            button.setEnabled(true);    // 设置按钮可点击
            button.setOnClickListener(/* ... */);  // 设置点击事件
        }
    });
});
javascript 复制代码
// 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()

javascript 复制代码
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

java 复制代码
// Xposed 方式创建对象
String str = (String) XposedHelpers.newInstance(
    String.class,  // 类
    "Hello"       // 参数
);

ArrayList list = (ArrayList) XposedHelpers.newInstance(
    ArrayList.class  // 类
);

简单说就是三步:

  • 找到类 (findClass)
  • 创建实例 (newInstance)
  • 调用方法 (callMethod)
java 复制代码
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 服务器库。

相关推荐
诺离24 分钟前
让Android adb支持互联网调试脱离局域网
android·adb
袁震2 小时前
Android-okhttp详解
android·okhttp
练小杰2 小时前
【MySQL】我在广州学Mysql 系列——MySQL用户管理详解
android·数据库·经验分享·sql·学习·mysql·adb
有趣的灵魂222斤4 小时前
如何获取svg图标中的路径 (漫反射图标效果实现)
android·view·material3·漫反射·svg路径
苏金标15 小时前
android 的aab包
android
aaajj15 小时前
【Android】问deepseek存储访问
android
不停留17 小时前
文本左右对齐
android·java·javascript·数据结构·算法
雾里看山18 小时前
【MySQL】 数据类型
android·数据库·mysql
Channing Lewis19 小时前
kotlin 简介
android·开发语言·kotlin
Blue.ztl19 小时前
菜鸟之路Day10一一集合进阶(三)
android·java·开发语言