Skip to content

任何人可自由编写的 映射库/注入库 for RW

License

Notifications You must be signed in to change notification settings

Minxyzgo/Rw-Injection

Repository files navigation

Rw-Injection - 适用于Rw的注入库


Kotlin

支持的功能:

  • 使用一套基于javassist的代理服务,支持静态/动态代理

TODO

  • 支持加载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

injectRwLib是插件提供的一个方便的函数,它只能在dependencies{}内调用

它的定义如下injectRwLib(version: String, useRuntimeLib: Boolean)

其中version是依赖的rwij版本, useRuntimeLib决定是否启用动态代理模式,默认为false,将在之后讲解

该函数不是必须的,仅当你有代理需求时,才应使用。若你只想使用rwij的jadx或反混淆功能,则可忽略不用

injection

injection是插件提供的dsl,是静态代理模式的实现,它可以很方便地进行诸如反混淆,加载jadx和代理操作

injection提供下列函数和字段

setProxy -- 代理解决方案

setProxy(lib: Libs, vararg proxyList: Any)设置指定Lib内的某个class为代理,当使用这个函数时,必须确保已经使用injectRwLib

proxyList指代理列表,可以传入class name批量实现代理,下面为一个示例

setProxy(Libs.`game-lib`, "a.a.b", "a.a.a")

这将代理classa.a.ba.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

deobfuscation传入一个classTree,它会重命名所有与包名冲突的类,如果该包没有这样的类则可以忽略不用

一个示例是

deobfuscation(Libs.`game-lib`.classTree)

initJadx

fun initJadx(
    fileName: String,
    dir: String = projectDir,
    lib: Libs = Libs.`game-lib`,
    otherTree: Array<Libs> = emptyArray()
)

其中fileName是jadx项目文件名(不包含.jadx后缀),默认从项目路径开始寻找

这将加载jadx项目文件并为指定lib进行重命名操作

action

如果你试图在构建时进行其它的操作 (运用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子项目中, 并确保多平台共享子项目的sourcecommonMain

要使用多平台,使用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代表两个平台, (目前仅支持jvmandroid

target 表示platformsource目标,例如desktop, desktopMain, jvmMain

其余配置与injection一致

About

任何人可自由编写的 映射库/注入库 for RW

Resources

License

Stars

Watchers

Forks

Packages

No packages published