当前位置: 首页 > article >正文

Xposed-Hook

配置 Xposed 模块的 AndroidManifest.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 模块的主类的完整类名(包含包名):

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 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 的一个内部类,以下是它的所有成员变量:

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,        // 参数类型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() { ... }
);

获取/设置字段值

// 获取字段值
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 {
    @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 接口中唯一需要实现的方法。它的作用是:

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 最常用的方法


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

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

  • View 就像是一个"容器"
  • 通过 cast 告诉系统:"这个容器其实是个按钮"
  • 转换后就可以用按钮特有的功能了
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(/* ... */);  // 设置点击事件
        }
    });
});
// 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 服务器库。


http://www.kler.cn/a/523414.html

相关文章:

  • FortiOS 存在身份验证绕过导致命令执行漏洞(CVE-2024-55591)
  • 机器学习(三)
  • Winform如何取消叉号,减号和放大(两种)
  • WPF基础 | WPF 常用控件实战:Button、TextBox 等的基础应用
  • 【时时三省】(C语言基础)文件的随机读写
  • fps一些内容添加
  • 每日一题 430. 扁平化多级双向链表
  • Nginx 性能优化技巧与实践(二)
  • GORM 支持的数据库解析
  • 力扣 【99. 恢复二叉搜索树】Java题解(二叉树的 Morris 遍历)
  • Vue.js 配合 Vue Router 使用 Vuex
  • 【漫话机器学习系列】065.梯度(Gradient)
  • 数组at()方法:负索引的救赎与JavaScript标准化之路
  • jemalloc 5.3.0的tsd模块的源码分析
  • 关于存储磁盘固件版本:打破版本一致性迷思
  • Python 函数魔法书:基础、范例、避坑、测验与项目实战
  • 大模型概述
  • 第一个3D程序!
  • 在虚拟机里运行frida-server以实现对虚拟机目标软件的监测和修改参数(一)(android Google Api 35高版本版)
  • 借DeepSeek-R1东风,开启创业新机遇
  • nosql mysql的区别
  • SQL server 数据库使用整理
  • 实时数据处理与模型推理:利用 Spring AI 实现对数据的推理与分析
  • 29. 【.NET 8 实战--孢子记账--从单体到微服务】--项目发布
  • 如何保证缓存与数据库的数据一致性?
  • 《多线程基础之条件变量》