RoxyHook 是面向 LibXposed API 102 的 Kotlin 封装与开发工具链。它提供平台无关的 Hook 核心、Android 生命周期与通信能力、LibXposed 平台适配、KSP 入口生成器,以及用于模块工程的 Gradle 插件。
本项目只面向 LibXposed API 102 及更高版本;不实现传统 XposedBridge、zygote 注入或 XResources 兼容层。
| 模块 | 用途 |
|---|---|
roxy-annotations |
@RoxyEntry 模块入口注解。 |
roxy-core |
Hook DSL、运行时、作用域、偏好与平台抽象。 |
roxy-android |
Android 生命周期、模块资源与跨进程数据通道。 |
roxy-platforms:libxposed |
LibXposed API 102 平台适配、Service 与热重载接入。 |
roxy-ksp |
KSP 符号处理器;生成 Xposed 入口和元数据。 |
roxy-gradle-plugin |
模块工程插件:自动依赖、元数据、R8 keep rules 和 APK 校验。 |
roxy-testing |
平台无关 Hook 行为的契约与回归测试。 |
samples:demo-module |
可编译的 LibXposed 模块 APK 示例。 |
samples:demo-target |
被 Hook 的目标应用示例。 |
- JDK 21
- Android SDK Platform 37
- Android
minSdk26 - 可访问 Google Maven、Maven Central 和 Gradle Plugin Portal 的网络
仓库携带标准 Gradle Wrapper(Gradle 9.5.0):
./gradlew --version
./gradlew projectsWindows 下可使用:
gradlew.bat --version
gradlew.bat projects./gradlew -PjvmOnly=true :roxy-testing:check :roxy-ksp:build :roxy-annotations:build :roxy-core:buildroxy-testing 会执行 Hook 契约与回归测试;它们是直接运行的测试套件,不依赖 JUnit 测试发现。
./gradlew \
:samples:demo-target:assembleDebug \
:samples:demo-module:assembleDebug \
:samples:demo-module:assembleRelease产物默认位于:
samples/demo-target/build/outputs/apk/debug/demo-target-debug.apk
samples/demo-module/build/outputs/apk/debug/demo-module-debug.apk
samples/demo-module/build/outputs/apk/release/demo-module-release-unsigned.apk
从仓库根目录运行:
./gradlew :samples:demo-module:roxyVerifyApk \
--apk=samples/demo-module/build/outputs/apk/debug/demo-module-debug.apk
./gradlew :samples:demo-module:roxyVerifyApk \
--apk=samples/demo-module/build/outputs/apk/release/demo-module-release-unsigned.apk该任务验证 APK 中的 LibXposed 元数据和生成入口类是否存在于 DEX;它不替代 APK 签名检查、框架加载、设备注入或真实 Hook 行为验证。
python3 tools/audit-source.py
./gradlew build源码仓库内的示例通过 includeBuild("roxy-gradle-plugin") 使用插件。对于独立工程,应在 settings.gradle.kts 同时配置插件与依赖仓库:
pluginManagement {
repositories {
maven("https://jitpack.io")
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
maven("https://jitpack.io")
google()
mavenCentral()
}
}然后在模块的 build.gradle.kts 中应用插件:
plugins {
id("com.android.application")
id("hk.uwu.roxyhook") version "<tag-or-commit>"
}
roxy {
scope.add("com.example.target")
staticScope.set(false)
}插件会为 Android application 模块接入 RoxyHook 平台、KSP 处理器、LibXposed API 和每个变体的元数据。入口使用 @RoxyEntry 标注继承 RoxyModule 的顶层类型。
同一个 Hook 可以组合 before、replaceAny(含 replaceTo、replaceUnit)和 after:先执行 before
,再执行替换函数,最后执行 after。before 若设置 result 或 throwable 主动短路,则跳过替换函数,但
after 仍执行;替换失败而采用回退策略时,after 也会看到回退后的结果或异常。
LibXposed 不会在热重载后重新发送 package 事件。模块可实现 LibXposedHotReload,并使用
LibXposedPackageReplay 保存已到达的作用域,在新一代明确地重新安装 Hook:
private val replay = LibXposedPackageReplay()
override fun PackageScope.onLoad() { replay.record(this); install(this) }
override fun prepareHotReload(runtime: RoxyRuntime, param: HotReloadingParam) = replay.prepare(param)
override fun installAfterHotReload(runtime: RoxyRuntime, process: ProcessContext, param: HotReloadedParam) {
replay.replay(runtime, process, param, ::install)
}作用域快照只存 Android 框架值;目标 ClassLoader 从旧 Hook
的目标方法所属类恢复,不能唯一确定时跳过该作用域并记录警告,不会猜测或复用旧模块对象。仍需在
prepareHotReload 自行停止模块创建的非托管线程和 JNI 回调。
如需从已有模块的热重载实现迁移,可向 LibXposedPackageReplay 传入原有 Bundle 键前缀(例如
"my.module.reload.")以兼容进行中的旧代状态。
import hk.uwu.roxyhook.HotReloadPolicy
runtime.hook(method) {
hotReloadPolicy = HotReloadPolicy.KEEP
before { /* 不依赖会在热重载时释放的资源 */ }
}
runtime.hookAll(methods) {
hotReloadPolicy = HotReloadPolicy.KEEP
after { /* 每个选中方法独立保留 */ }
}无需填写 id。同一模块、同一实际目标方法的匿名 KEEP 使用一个自动槽位;再次安装会复用已有 Hook,
不会替换它的回调。只有要在同一个方法上保留多个独立 Hook 时才需用不同的可选 id 区分。
类名相同但 ClassLoader 不同的目标不会混用;不依赖源码行号、lambda 类名或集合顺序。
- 默认
REINSTALL仍沿用现有流程,重装依赖模块的installAfterHotReload/ replay;KEEP 不会自动开启模块热重载。 - KEEP 的代码、错误策略保持首次安装版本;重复声明的 priority 必须一致。新 DSL 配置块仍会执行,但新回调不会安装。
hookAll支持重复成员去重、成员重排、交叠集合,以及从单 Hook 改为批量。新增成员首次安装,未再选中的旧 KEEP 不会自动卸载。- 批量失败仅回滚本次新装的成员,不卸载复用的旧 KEEP。成功后的
HookGroup.close()则卸载组内全部成员;交叠组共享 handle,关闭一组会使另一组共享的成员失效。 - 当前代 handle 的
remove()/close()和 runtime 的普通close()可卸载 KEEP;旧代已移交的包装不再操作底层注册。KEEP 暂不支持replace()、回调内removeSelf()或类初始化器 Hook。 - 不支持 KEEP 的平台会显式拒绝。用户
id不可使用roxy.keep.保留前缀。 - 从匿名 KEEP 改为匿名 REINSTALL 不构成替换,会成为两条注册;需先显式移除旧 KEEP 或重启目标进程。删除声明也不等于卸载。
**生命周期代价:**保留的回调仍来自旧模块 ClassLoader,会阻止该代立即回收。不要捕获旧
runtime、scope、Activity,
或依赖 onDispose / quiesce 会关闭的订阅、线程、JNI、服务等资源;这些资源不会因 KEEP 而保留。
推荐只保留无外部生命周期依赖的参数/返回值处理。修改 KEEP 实现、首次升级到此功能或协议降级后应重启目标进程。
HookOptions 扩展可能影响旧二进制调用点,下游模块应重新编译,不承诺 data class 的完整二进制兼容。
**验证边界:**该功能尚需针对实际执行框架验证连续 A→B→C 重载:每一代的 oldHookHandles 必须包含更早代仍存活的
KEEP。
API 接口本身不保证这种跨多代枚举;JVM/模拟平台测试不能替代设备验证。未完成该验证前,不应将 KEEP
视为已支持的生产能力。
若框架漏报旧 handle,不能靠 Bundle 携带旧模块对象来绕过,应由执行框架补足契约或重启目标进程。
roxy-core 提供 hk.uwu.roxyhook.RLog 门面,模块代码可在任意位置直接调用,无需持有运行时或平台引用:
RLog.info("module loaded")
RLog.error("hook failed", throwable)路由规则:每次调用从当前打开的注入运行时(platform.info.isInjected)取得平台 logger,因此注入场景下消息进入框架日志;无活动注入运行时(模块自身进程、运行时已关闭、纯 JVM 测试)时回退到 RoxyLogger.STDERR,不会静默丢弃。PackageScope.log 走同一派发路径并自动附加 [包名/进程名] 前缀。RLog 仅弱引用运行时快照,不会产生全局强引用。
PackageScope 统一暴露当次注入事件的常用上下文,减少每个 Hook 里重复保管的样板代码:
| API | 语义 / 前置条件 |
|---|---|
mainProcessName / isMainProcess / processName |
包声明的主进程名与当前进程名。 |
appInfo |
当次 package 事件的 ApplicationInfo 防御性快照;system_server 或无平台数据时为 null。 |
application / appContext / appResources |
宿主 Application attach 之后可用;attach 前为 null,system_server 中读取会失败。 |
systemContext |
仅 system_server。经 SystemContextResolver 调用 ActivityThread 隐藏 API;失败抛出带原因的 IllegalStateException,无回退。 |
moduleAppFile |
模块自身 APK File(LibXposed moduleApplicationInfo.sourceDir);平台未提供时失败。 |
moduleResources |
模块资源;app 进程用 appContext、system_server 用 systemContext 作宿主 Context,未就绪即失败。 |
dataChannel |
跨进程数据通道;自动取 appContext,校验包名一致且非 system_server,receiver 由 runtime 管理。使用 put 发送、receive 注册监听、waitFor 请求回复;通道按包路由且不提供认证,请勿发送敏感数据。 |
prefs / prefs() |
默认远程偏好组 "default";prefs(group) 指定命名组。需要框架 REMOTE_PREFERENCES 能力。 |
systemContext 依赖 ActivityThread.currentActivityThread()/getSystemContext() 隐藏 API(与 YukiHookAPI 相同机制)。该路径受 Android 隐藏 API 限制影响,属于框架授予的受控例外,仅限 system_server;解析失败会显式抛出并携带底层原因,不会静默回退到其它 Context。
./gradlew publishAllToMavenLocal
./gradlew -p roxy-gradle-plugin publishToMavenLocal第一条命令发布所有 JVM JAR 与 Android AAR;第二条命令发布 Gradle 插件及其 plugin marker。
仓库根目录的 jitpack.yml 使用 JDK 21,并会发布库与 Gradle 插件。推送到 GitHub 后,为提交或 tag 在 JitPack 页面触发构建:
https://jitpack.io/#NekoStash/RoxyHook
JitPack 产物坐标遵循:
com.github.NekoStash.RoxyHook:<artifact>:<tag-or-commit>
JitPack 构建的实际可用性应以该远程构建日志为准。
本项目采用 Apache License 2.0 许可证。第三方依赖、Gradle Wrapper 和工具组件受各自许可证约束;请在分发前审阅相关上游许可证与归属信息。