Android Hook 机制分析
一、Hook 是什么
在运行时替换或拦截原有方法/函数的调用,使执行流程走到自定义逻辑,再决定是否继续调用原实现。
二、常见 Hook 实现方式
1. Java 反射 Hook
思路:通过反射修改对象字段或替换方法实现。
val activityThreadClass = Class.forName("android.app.ActivityThread")
val currentActivityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null)
val mHField = activityThreadClass.getDeclaredField("mH")
mHField.isAccessible = true
val mH = mHField.get(currentActivityThread) as Handler
val callbackField = Handler::class.java.getDeclaredField("mCallback")
callbackField.isAccessible = true
callbackField.set(mH, Handler.Callback { msg ->
if (msg.what == 100) {
return@Callback true
}
false
})
| 步骤 | 说明 |
|---|
| 1 | 用反射拿到目标类/对象(如 ActivityThread、mH) |
| 2 | 修改字段(如 mCallback)或替换为代理对象 |
| 3 | 后续调用会先经过代理,再决定是否调用原实现 |
2. 动态代理 Hook
思路:用 Proxy.newProxyInstance 生成接口的代理实现,在 InvocationHandler 里拦截方法。
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val iClipboard = ClipboardManager::class.java.getDeclaredField("mService")
.apply { isAccessible = true }
.get(clipboard)
val proxy = Proxy.newProxyInstance(
iClipboard.javaClass.classLoader,
arrayOf(Class.forName("android.content.IClipboard"))
) { _, method, args ->
if (method.name == "getPrimaryClip") {
return@newProxyInstance null
}
method.invoke(iClipboard, *(args ?: arrayOfNulls(0)))
}
3. 替换 ClassLoader / Dex 元素 Hook
思路:在类加载前替换字节码或 Dex 中的方法实现(如通过自定义 ClassLoader、Gradle 插件等)。
class HookClassLoader(parent: ClassLoader) : ClassLoader(parent) {
override fun loadClass(name: String, resolve: Boolean): Class<*> {
if (name == "target.ClassName") {
return defineClass(name, modifiedBytecode, 0, modifiedBytecode.size)
}
return super.loadClass(name, resolve)
}
}
| 特点 | 说明 |
|---|
| 时机 | 在类加载前介入 |
| 实现 | 需要字节码操作(ASM、Javassist 等)或自定义 ClassLoader |
4. Native Hook(C/C++ 层)
思路:修改函数入口的机器码,跳转到自定义函数(inline hook、GOT/PLT hook 等)。
| 常见库 | 用途 |
|---|
| Substrate、Frida | 动态插桩、函数替换 |
| PLT Hook | 修改 GOT 表,拦截动态链接函数 |
三、典型 Hook 点(Android)
| 目标 | 作用 |
|---|
ActivityThread.mH | 拦截 Activity 生命周期、启动等消息 |
ActivityThread.mInstrumentation | 拦截 Activity 创建、生命周期调用 |
PackageManager、ClipboardManager 等 Binder 代理 | 拦截系统服务调用 |
View.mAttachInfo.mSession | 拦截 Window 相关操作 |
Resources / AssetManager | 换肤、资源替换 |
InputMethodManager | 输入法相关逻辑 |
四、优点
| 优点 | 说明 |
|---|
| 无侵入 | 不改业务源码,通过 Hook 统一处理 |
| 灵活 | 可拦截系统 API、第三方 SDK,扩展能力强 |
| 集中控制 | 在少数点统一实现埋点、权限控制、兼容等 |
| 可逆 | 可恢复原实现,便于调试和回滚 |
| 兼容老版本 | 在不改 APK 的前提下适配不同系统行为 |
五、缺点
| 缺点 | 说明 |
|---|
| 稳定性差 | 依赖系统内部实现,ROM/版本升级易失效或崩溃 |
| 维护成本高 | 需适配各厂商、各 Android 版本,测试量大 |
| 性能开销 | 反射、代理有额外调用成本,高频路径需谨慎 |
| 安全风险 | 易被检测,部分场景违反应用商店或合规要求 |
| 调试困难 | 调用链被改写,堆栈不直观,问题难定位 |
| 兼容性 | 不同 ROM 可能加固、混淆,类名/字段名变化 |
六、使用建议
| 场景 | 建议 |
|---|
| 埋点、监控 | 优先用 AOP(AspectJ 等)、编译期插桩,少用运行时 Hook |
| 换肤、资源替换 | 可 Hook Resources / AssetManager,但要做好版本兼容 |
| 系统行为适配 | 评估是否可用官方 API 或配置替代,再考虑 Hook |
| 安全/合规要求高 | 避免 Hook 系统核心逻辑,优先正规 API |
七、小结
| 维度 | 说明 |
|---|
| 本质 | 运行时替换/拦截调用链,插入自定义逻辑 |
| 常见手段 | 反射改字段、动态代理、字节码替换、Native Hook |
| 优点 | 无侵入、灵活、可集中控制 |
| 缺点 | 稳定性依赖实现细节、维护成本高、有性能和安全风险 |
结论:Hook 适合做兼容、监控、扩展,但应控制范围和调用频率,并做好版本适配与回退方案。