alesimula

RedefineUtils.kt

May 16th, 2020
271
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. package com.greenapple.glacia.utils
  2.  
  3. import com.ea.agentloader.AgentLoader
  4. import javassist.*
  5. import java.io.Serializable
  6. import java.lang.instrument.ClassDefinition
  7. import java.lang.instrument.Instrumentation
  8. import java.util.concurrent.atomic.AtomicLong
  9. import kotlin.reflect.KCallable
  10. import kotlin.reflect.KClass
  11. import kotlin.reflect.KFunction
  12.  
  13. private typealias TMethodNoReturn<O> = TMethod<O, Unit>
  14. private typealias TMethod<O, R> = O.(@ParameterName("args") Array<out Any?>) -> R
  15.  
  16. object RedefineUtils {
  17.     fun interface IMethod<O, R>: (O, Array<out Any?>) -> R, Serializable {
  18.         override fun invoke(receiver: O, vararg args: Any?): R
  19.     }
  20.     private class RedefineAgent {
  21.         companion object {
  22.             @JvmStatic private lateinit var instrumentationInternal: Instrumentation
  23.             @JvmStatic val instrumentation by lazy {
  24.                 ClassLoader.getSystemClassLoader().loadClass(RedefineAgent::class.java.name).getDeclaredField(::instrumentationInternal.name).apply {isAccessible = true}[null] as Instrumentation
  25.             }
  26.             @JvmStatic fun agentmain(agentArgs: String?, instr: Instrumentation) {
  27.                 instrumentationInternal = instr
  28.             }
  29.         }
  30.     }
  31.  
  32.     @JvmStatic val instrumentation by lazy {
  33.         AgentLoader.loadAgentClass(RedefineAgent::class.java.name, null)
  34.         RedefineAgent.instrumentation
  35.     }
  36.  
  37.     class FallBackException : Exception()
  38.     fun fallback(): Nothing = throw FallBackException()
  39. }
  40.  
  41. private val nextIndex = AtomicLong(0)
  42.  
  43. private inline fun <O : Any> KClass<O>.editClassDef(block: CtClass.(@ParameterName("instrumentation") Instrumentation) -> Unit) = try {
  44.     ClassPool.getDefault().apply {appendClassPath(LoaderClassPath(Thread.currentThread().contextClassLoader))}[qualifiedName]?.apply {
  45.         defrost()
  46.         block(this, RedefineUtils.instrumentation)
  47.         RedefineUtils.instrumentation.redefineClasses(ClassDefinition(this@editClassDef.java, this.toBytecode()))
  48.     } ?: System.err.println("Class $qualifiedName not found")
  49. } catch (ex: Exception) {ex.printStackTrace()}
  50.  
  51. private inline fun <O : Any> KClass<O>.editMethodDef(methodName: String, block: CtMethod.(@ParameterName("instrumentation") Instrumentation) -> Unit) = editClassDef { instrumentation->
  52.     block(getDeclaredMethod(methodName), instrumentation)
  53. }
  54.  
  55. private fun <O : Any> CtMethod.newStaticMethodCall(function: TMethod<O, Any>, shouldReturn: Boolean = false) = StringBuffer().apply {
  56.     val extrasClass = "${this@newStaticMethodCall.declaringClass.name}\$extras_${nextIndex.getAndIncrement()}"
  57.     ClassPool.getDefault().makeClass(extrasClass).apply {
  58.         val iMethod = RedefineUtils.IMethod(function)
  59.         addField(CtField.make("public static ${RedefineUtils.IMethod::class.java.name} __callable__ = null;\n", this))
  60.         toClass().getDeclaredField("__callable__").set(null, iMethod)
  61.     }
  62.     append("{\nObject __returnValue__ = $extrasClass.__callable__.invoke($0, \$args);\n")
  63.     if (shouldReturn && returnType.name != "void")
  64.         append("return ((${(returnType as? CtPrimitiveType)?.wrapperName ?: returnType.name})(__returnValue__))${if (returnType.isPrimitive) ".${returnType.name}Value()" else ""};\n")
  65.     else if (shouldReturn) append("return;\n")
  66.     append("}")
  67. }.toString()
  68.  
  69. private fun String.wrapTryOrFallback() = "try $this catch (${RedefineUtils.FallBackException::class.java.name} __fallbackException__) {}"
  70.  
  71. /**
  72.  * Appends a function at end of a method
  73.  */
  74. fun <O : Any> KClass<O>.addMethodAfter(method: KCallable<*>, function: TMethodNoReturn<O>) = addMethodAfter(method.name, function)
  75. fun <O : Any> KClass<O>.addMethodAfter(methodName: String, function: TMethodNoReturn<O>) = editMethodDef(methodName) {
  76.     insertAfter(newStaticMethodCall(function))
  77. }
  78.  
  79. /**
  80.  * Appends a function at the start of a method
  81.  */
  82. fun <O : Any> KClass<O>.addMethodBefore(method: KCallable<*>, function: TMethodNoReturn<O>) = addMethodBefore(method.name, function)
  83. fun <O : Any> KClass<O>.addMethodBefore(methodName: String, function: TMethodNoReturn<O>) = editMethodDef(methodName) {
  84.     insertBefore(newStaticMethodCall(function))
  85. }
  86.  
  87. /**
  88.  * Replaces a method
  89.  * Must call RedefineUtils.fallback() to revert to default behaviour
  90.  */
  91. fun <O : Any, R : Any> KClass<O>.replaceMethodOrFallback(method: KCallable<*>, function: TMethod<O, R>) = replaceMethodOrFallback(method.name, function)
  92. fun <O : Any> KClass<O>.replaceMethodOrFallback(methodName: String, function: TMethod<O, Any>) = editMethodDef(methodName) {
  93.     insertBefore(newStaticMethodCall(function, true).wrapTryOrFallback())
  94. }
  95.  
  96. /**
  97.  * Replaces a method
  98.  */
  99. fun <O : Any, R : Any> KClass<O>.replaceMethod(method: KFunction<R>, function: TMethod<O, R>) = replaceMethod(method.name, function)
  100. fun <O : Any> KClass<O>.replaceMethod(methodName: String, function: TMethod<O, Any>) = editMethodDef(methodName) {
  101.     setBody(newStaticMethodCall(function, true))
  102. }
RAW Paste Data