为什么第一个 hook 点永远是 Application.onCreate
写 Xposed 模块逆向 App,90% 的功能需要 Context(拿包管理器、访问 SharedPreferences、弹 Toast 调试)。而模块加载时(handleLoadPackage)是没有 Context 的——进程刚启动,Application 都还没创建。所以所有 Xposed 模块的第一个标准动作:hook Application.onCreate,在回调里拿 Context,再干正事。
模块骨架
public class MainHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(final LoadPackageParam lpparam) {
if (!lpparam.packageName.equals("com.target.app")) return;
// 只 hook 主进程,多进程 App 会触发多次
if (!lpparam.processName.equals(lpparam.packageName)) return;
XposedHelpers.findAndHookMethod(Application.class, "onCreate",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
Context ctx = (Context) param.thisObject;
XposedBridge.log("拿到 Context: " + ctx.getPackageName());
doWork(ctx, lpparam.classLoader); // 正事从这里开始
}
});
}
}精髓一:ClassLoader 是模块开发的第一道坎
lpparam.classLoader 是目标 App 的类加载器,hook 目标 App 的类必须用它:
// 错误:XposedHelpers.findClass 默认用模块自己的 ClassLoader,找不到目标 App 的类
// 正确:
Class<?> targetClass = XposedHelpers.findClass("com.target.app.SignUtils", lpparam.classLoader);加固 App 还有第二层坑:壳的 onCreate 执行时,真正的 dex 还没加载,lpparam.classLoader 里找不到业务类。解法是延迟 hook——hook 壳的 attachBaseContext 之后、或 hook ClassLoader.loadClass 等目标类出现时再下手:
XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
String name = (String) param.args[0];
if (name.equals("com.target.app.SignUtils")) {
Class<?> cls = (Class<?>) param.getResult();
hookSign(cls); // 类加载完成的瞬间 hook 它
}
}
});精髓二:hook 方法的三种姿势与选择
// 1. 无参/已知签名:findAndHookMethod 最简洁
XposedHelpers.findAndHookMethod("com.target.app.SignUtils", lpparam.classLoader,
"getSign", String.class, new XC_MethodHook() { ... });
// 2. 重载爆炸的方法:遍历所有重载
Class<?> cls = XposedHelpers.findClass("com.target.app.Api", lpparam.classLoader);
XposedBridge.hookAllMethods(cls, "request", new XC_MethodHook() { ... });
// 3. 构造函数也能 hook:拿对象初始化状态
XposedBridge.hookAllConstructors(cls, new XC_MethodHook() { ... });拿参数、改参数、改返回值:
protected void beforeHookedMethod(MethodHookParam param) {
String input = (String) param.args[0]; // 拿参数
param.args[0] = "篡改后的值"; // 改参数
// param.setResult("伪造返回"); // 直接短路,原方法不执行
}
protected void afterHookedMethod(MethodHookParam param) {
Object ret = param.getResult(); // 拿返回值
param.setResult(ret + "-modified"); // 改返回值
}精髓三:调试与排错
- 日志双写:
XposedBridge.log()进 Xposed 日志,android.util.Log进 logcat。模块崩了先看 Xposed Installer 的日志页,堆栈完整。 - 作用域收窄:
handleLoadPackage里先判包名再判进程,否则模块会在系统所有进程里加载,又慢又容易崩系统 App。 - 资源冲突:模块里引用的第三方库如果和目标 App 版本冲突,会 NoSuchMethodError。用 Gradle 的
compileOnly或 shadow 重定位包名解决。 - EdXposed/LSPosed 的作用域机制:LSPosed 里模块只对勾选的作用域生效,调试时把"系统框架"也勾上能避免一些诡异问题。
总结
- 一切从 hook Application.onCreate 拿 Context 开始
- 目标类必须用 lpparam.classLoader 加载,加固 App 要延迟 hook
- hookAllMethods 应对混淆重载,before/after 分工明确
- 日志双写 + 作用域收窄,是稳定性的基本功
交流微信:run1255