一、Xposed 框架原理
Xposed 是目前最强大的 Android Java 层 Hook 框架。其核心原理是劫持 Zygote 进程 —— 在 Android 启动过程中,Zygote 是所有应用进程的父进程,Xposed 向 Zygote 注入自己的 app_process,使得每个新应用进程启动时都会加载 XposedBridge.jar。
/system/bin/app_process → Xposed 修改后的 app_process → ZygoteInit.java (frameworks/base/core/java/com/android/internal/os/ZygoteInit.java) → XposedBridge.jar 加载 → 读取模块列表 → 为每个已注册的 Hook 方法安装拦截器
|
二、XposedBridge API 详解
Xposed 的核心 API 在 de.robv.android.xposed 包中:
XposedHelpers
Class<?> cls = XposedHelpers.findClass("com.target.MyClass", classLoader); XposedHelpers.findAndHookMethod("com.target.MyClass", classLoader, "targetMethod", String.class, int.class, new XC_MethodHook() { ... });
|
XC_MethodHook
new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { String arg0 = (String) param.args[0]; XposedBridge.log("Called with arg: " + arg0); param.args[0] = "modified_value"; }
@Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { boolean originalResult = (boolean) param.getResult(); param.setResult(true); } };
|
XC_MethodReplacement
XposedHelpers.findAndHookMethod("com.target.MyClass", classLoader, "checkLicense", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) { return true; } });
|
三、Xposed 模块开发
一个完整的 Xposed 模块包含:
1. 模块 Manifest 声明:
<application> <meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposeddescription" android:value="Bypass license check for TargetApp" /> <meta-data android:name="xposedminversion" android:value="82" /> </application>
|
2. Hook 入口类:
public class MainHook implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { if (!lpparam.packageName.equals("com.target.app")) return;
XposedHelpers.findAndHookMethod( "com.target.app.VerifyActivity", lpparam.classLoader, "verify", String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) { param.setResult("success"); } } ); } }
|
3. 声明入口(assets/xposed_init):
com.example.xposedmodule.MainHook
|
四、实战案例
绕过 String.equals 检测
XposedHelpers.findAndHookMethod(String.class, "equals", Object.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { XposedBridge.log("String.equals: [" + param.thisObject + "] vs [" + param.args[0] + "]"); } });
|
Hook 加密操作
XposedHelpers.findAndHookMethod( "javax.crypto.Cipher", lpparam.classLoader, "init", int.class, Key.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { SecretKeySpec key = (SecretKeySpec) param.args[1]; XposedBridge.log("Cipher key: " + bytesToHex(key.getEncoded())); } });
|
绕过 Root 检测
XposedHelpers.findAndHookMethod(File.class, "exists", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) { String path = ((File) param.thisObject).getAbsolutePath(); if (path.contains("su") || path.contains("Superuser")) { param.setResult(false); } } });
|
五、Xposed vs EdXposed vs LSPosed
| 特性 |
Xposed (Original) |
EdXposed |
LSPosed |
| 原理 |
替换 app_process |
Riru / Zygisk 注入 |
Zygisk 注入 |
| Android 版本 |
5.0 - 8.x |
9 - 12 |
8.1 - 14 |
| 检测难度 |
极易被检测 |
中等 |
较难检测 |
| 维护状态 |
停止更新 |
停止更新 |
活跃维护 |
| 仓库 |
- |
github.com/ElderDrivers/EdXposed |
github.com/LSPosed/LSPosed |
面试常考问题
Q1:Xposed Hook 和 Frida Hook 的区别?各自适用场景?
A:Xposed 工作在 Zygote 层,模块生命周期与应用绑定,Hook 是持久化的,更适合长期监控和模块化发布。Frida 通过注入 JS 引擎实现动态 Hook,无需重启设备,更灵活,适合临时调试和快速验证。Xposed 适合开发可复用的 Hook 模块,Frida 适合逆向分析时的即时探索。此外,Xposed 仅支持 Java 层,Frida 同时支持 Java 层和 Native 层。
Q2:为什么有些应用能检测到 Xposed?如何绕过?
A:常见检测方式:(1)检查 ClassLoader 中是否加载了 de.robv.android.xposed.XposedBridge;(2)通过 PackageManager 查找 Xposed Installer 包名;(3)检测 /system/lib/libxposed_art.so 等文件存在性;(4)抛异常检查调用栈中是否包含 Xposed 相关类。绕过手段:使用 LSPosed(支持隐藏)、Magisk + Shamiko 隐藏 Xposed 特征、在模块中 Hook 检测方法返回假值。
Q3:beforeHookedMethod 和 afterHookedMethod 的执行顺序是怎样的?如果多个模块 Hook 了同一个方法?
A:beforeHookedMethod 在原始方法执行前调用,afterHookedMethod 在原始方法执行后调用。多个模块 Hook 同一方法时,Xposed 按模块加载顺序链式调用:模块A的 before → 模块B的 before → 原始方法 → 模块B的 after → 模块A的 after。注意在 beforeHookedMethod 中调用 param.setResult() 会跳过原始方法和后续 Hook 链的 before。