- 使用一套基于javassist的代理服务,支持静态/动态代理
- 支持加载jadx
- gradle-插件
- 支持多平台 (Jvm, Android)
build.gradle.kts 添加如下代码
buildscript {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
dependencies {
classpath("com.github.minxyzgo.rw-injection:com.github.minxyzgo.rwij.gradle.plugin:master-SNAPSHOT")
}
}
repositories {
mavenCentral()
maven("https://jitpack.io")
}
apply<com.github.minxyzgo.rwij.GradlePlugin>()
dependencies {
injectRwLib("master-SNAPSHOT")
}静态代理模式下,只会在编译期间在classpath内写入诸如javassist等依赖库,只会写入核心库core提供方便的代理方案
使用静态代理模式将极大减小运行期压力和缩小Jar所占空间大小,并且直接向classpath注入rw所需的lib,这将更为方便地兼容其它诸如android的平台
缺点: 运行期你将无法对类进行进一步修改
当调用apply<com.github.minxyzgo.rwij.GradlePlugin>()时,插件开始加载
注意: 为兼容jadx,必须每次初始化项目后都会释放rw lib,这可能会覆盖原本的lib,因此,如果你有特殊的lib需要使用,应用injection下的libMapping(之后提到)
injectRwLib是插件提供的一个方便的函数,它只能在dependencies{}内调用
它的定义如下injectRwLib(version: String, useRuntimeLib: Boolean)
其中version是依赖的rwij版本, useRuntimeLib决定是否启用动态代理模式,默认为false,将在之后讲解
该函数不是必须的,仅当你有代理需求时,才应使用。若你只想使用rwij的jadx或反混淆功能,则可忽略不用
injection是插件提供的dsl,是静态代理模式的实现,它可以很方便地进行诸如反混淆,加载jadx和代理操作
injection提供下列函数和字段
setProxy(lib: Libs, vararg proxyList: Any)设置指定Lib内的某个class为代理,当使用这个函数时,必须确保已经使用injectRwLib
proxyList指代理列表,可以传入class name批量实现代理,下面为一个示例
setProxy(Libs.`game-lib`, "a.a.b", "a.a.a")这将代理classa.a.b和a.a.a,之后便可以使用core内提供的函数来方便地进行代理,如何使用将在之后提到
此外proxyList还有十分方便的方法
若使用setProxy(Libs.`game-lib`, "empty:a.a.b")这意味者将a.a.b内的所有方法设置为空体,即所有方法不会包含任何实现,只会返回该方法的默认值
如函数返回类型为Object则默认返回null,类型为int则返回0,与java类型默认值一致,以此类推
若使用setProxy(Libs.`game-lib`, "empty:a.a.b".with("a"))表示a.a.b内方法名为a将会被设置为空体
同时,还可以同时设置多个方法,并且带签名,一个示例是setProxy(Libs.`game-lib`, "empty:a.a.b".with("a(IIZLjava/lang/String;)", “b”))
另外,如果不带empty:前缀,意味作用将更改为代理传入的函数
还有另一个方法是withNon(args..)它意味着除了传入的方法都会被设置为空体/代理,其余用法与with(args..)一致
deobfuscation传入一个classTree,它会重命名所有与包名冲突的类,如果该包没有这样的类则可以忽略不用
一个示例是
deobfuscation(Libs.`game-lib`.classTree)fun initJadx(
fileName: String,
dir: String = projectDir,
lib: Libs = Libs.`game-lib`,
otherTree: Array<Libs> = emptyArray()
)其中fileName是jadx项目文件名(不包含.jadx后缀),默认从项目路径开始寻找
这将加载jadx项目文件并为指定lib进行重命名操作
如果你试图在构建时进行其它的操作 (运用javassist) 那么action会很有用
一个示例是
action {
Libs.`game-lib`.classTree.defPool["xxx"].apply {
val method = getDeclaredMethod("x")
//...
}
}下面是一个综合以上示例的例子
buildscript {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
dependencies {
classpath("com.github.minxyzgo.rw-injection:com.github.minxyzgo.rwij.gradle.plugin:master-SNAPSHOT")
}
}
repositories {
mavenCentral()
maven("https://jitpack.io")
}
apply<com.github.minxyzgo.rwij.GradlePlugin>()
dependencies {
injectRwLib("master-SNAPSHOT")
}
injection {
deobfuscation(com.github.minxyzgo.rwij.Libs.`game-lib`)
initJadx("game-lib.jar")
setProxy(Libs.`game-lib`, "a.a.b", "a.a.c".with("c"), "a.a.d".withNon("a", "b(IZ)"))
action {
//...
}
}在你完成了配置以后,plugin提供了rebuildJartask,这将执行injection的内容,并输出jar到build/gerated/lib下
如果你想在运行期方便修改各种class,只需要injectRwLib("master-SNAPSHOT", true)启动动态代理
动态代理模式下,rw lib将不会自动写入classpath,而是写入resources,且自动导入javassist等需要的库
此时如果你需要代理一个Rw class的函数, 那么在程序中采取以下代码:
ProxyFactory.runInit {
setProxy("...")
setProxy("...")
}
//注意,runInit应当在程序生命周期中只调用一次其中setProxy用法同上面一致
当你使用injectRwLib时,无论静态代理还是动态代理都已经导入了core库,对一个函数进行代理则可以用以下方法
示例
class Example {
fun sample1() = 12345
fun sample2(i: Int)
} //请确保该类在setProxy中
val e = Example()
e.sample1() // 12345
Example::class.setFunction {
addProxy(Example::sample1) { // 自动推断为 (Example) -> Unit. 因此可以用idea自动补全
println(123)
}
addProxy(Example::sample2) { self, i -> // 自动推断为 (Example, Int) -> Unit. 因此可以用idea自动补全
println(i)
}
// 函数名 + 参数列表
addProxy("sample2", Int::class) { self: Example, i: Int -> // 等效于上面函数,但无法自动推断,可以自行填充参数
println(i)
}
// 函数名 + 签名
addProxy("sample2", "(I)") { self: Example, i: Int -> // 等效于上面函数,但无法自动推断,可以自行填充参数
println(i)
}
}
e.sample1() // 123
e.sample2(234) // 234
e.setFunction(e::sample1) {
println(234)
}
e.sample1() // 234因此,代理优先级为 对象代理 > 类代理 > 原始函数
如果你考虑支持除Jvm以外的平台, 那么多平台支持会解决这个问题
要使用多平台支持,请确保已有kotlin-multiplatformgradle插件
且插件应位于common子项目中, 并确保多平台共享子项目的source为commonMain
要使用多平台,使用injectionMultiplatform 代替 injection
injectionMultiplatform {
enable = true
jvm {
target = "desktop"
setProxy(Libs.`game-lib`, "xxx")
action {
//...
}
}
android {
setProxy(Libs.`android-game-lib`, "xxx")
//...
}
}上面的例子中, enable = true是必须的,否则多平台支持无法起效
其中jvm android代表两个平台, (目前仅支持jvm和android)
target 表示platform的source目标,例如desktop, desktopMain, jvmMain等
其余配置与injection一致