Added much functions and Merge code

This commit is contained in:
2022-03-27 23:03:54 +08:00
parent 99b6f041f0
commit 405079c18d
11 changed files with 496 additions and 280 deletions

View File

@@ -194,7 +194,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
}
File("$projectPath${separator}assets").also { assFile ->
if (File("$projectPath${separator}AndroidManifest.xml").exists()) {
if (!assFile.exists() || !assFile.isDirectory) {
if (assFile.exists().not() || assFile.isDirectory.not()) {
assFile.delete()
assFile.mkdirs()
}

View File

@@ -299,7 +299,7 @@ object YukiHookAPI {
/** 输出欢迎信息调试日志 */
private fun printSplashLog() {
if (!Configs.isDebug || !isShowSplashLogOnceTime || isModulePackageXposedEnv) return
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not() || isModulePackageXposedEnv) return
isShowSplashLogOnceTime = false
yLoggerI(msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using $executorName API $executorVersion")
}

View File

@@ -100,12 +100,12 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
*/
@DoNotUseMethod
fun hook(): Result {
if (!YukiHookAPI.hasXposedBridge) return Result()
if (YukiHookAPI.hasXposedBridge.not()) return Result()
if (hookMembers.isEmpty()) error("Hook Members is empty,hook aborted")
else Thread {
SystemClock.sleep(10)
if (!isDisableCreaterRunHook && hookClass.instance != null) hookMembers.forEach { (_, member) -> member.hook() }
if (!isDisableCreaterRunHook && hookClass.instance == null)
if (isDisableCreaterRunHook.not() && hookClass.instance != null) hookMembers.forEach { (_, member) -> member.hook() }
if (isDisableCreaterRunHook.not() && hookClass.instance == null)
if (onHookClassNotFoundFailureCallback == null)
yLoggerE(msg = "HookClass [${hookClass.name}] not found", e = hookClass.throwable)
else onHookClassNotFoundFailureCallback?.invoke(hookClass.throwable ?: Throwable("[${hookClass.name}] not found"))
@@ -360,7 +360,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
*/
@DoNotUseMethod
fun hook() {
if (!YukiHookAPI.hasXposedBridge || isDisableMemberRunHook) return
if (YukiHookAPI.hasXposedBridge.not() || isDisableMemberRunHook) return
if (hookClass.instance == null) {
(hookClass.throwable ?: Throwable("HookClass [${hookClass.name}] not found")).also {
onHookingFailureCallback?.invoke(it)

View File

@@ -35,8 +35,9 @@ package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Constructor
@@ -61,28 +62,50 @@ class ConstructorFinder(
/** [Constructor] 参数数组 */
private var paramTypes: Array<out Class<*>>? = null
/** [ModifierRules] 实例 */
private var modifiers: ModifierRules? = null
/**
* [Constructor] 参数个数
*
* 你可以不使用 [param] 指定参数类型而是仅使用此变量指定参数个数
*
* 若参数个数小于零则忽略并使用 [param]
*/
var paramCount = -1
/**
* [Constructor] 筛选条件
*
* 可不设置筛选条件 - 默认模糊查找并取第一个匹配的 [Constructor]
* @param initiate 方法体
*/
fun modifiers(initiate: ModifierRules.() -> Unit) {
modifiers = ModifierRules().apply(initiate)
}
/**
* [Constructor] 参数
*
* 如果同时使用了 [paramCount] 则 [paramTypes] 的数量必须与 [paramCount] 完全匹配
*
* - ❗无参 [Constructor] 不要使用此方法
*
* - ❗有参 [Constructor] 必须使用此方法设定参数
* - ❗有参 [Constructor] 必须使用此方法设定参数或使用 [paramCount] 指定个数
* @param paramType 参数类型数组
*/
fun param(vararg paramType: Class<*>) {
if (paramType.isEmpty()) error("paramType is empty, please delete param() method")
if (paramType.isEmpty()) error("paramTypes is empty, please delete param() method")
paramTypes = paramType
}
/**
* 得到构造方法
* @return [Constructor]
* @throws IllegalStateException 如果 [classSet] 为 null
* @throws NoSuchMethodError 如果找不到构造方法
*/
private val result
get() = if (paramTypes != null)
ReflectionUtils.findConstructorExact(classSet, *paramTypes!!)
else ReflectionUtils.findConstructorExact(classSet)
private val result get() = ReflectionTool.findConstructor(classSet, modifiers, paramCount, paramTypes)
/**
* 设置实例
@@ -170,7 +193,7 @@ class ConstructorFinder(
onFailureMsg(msg = "trying ${p + 1} times by RemedyPlan --> $it", isAlwaysPrint = true)
}
}
if (!isFindSuccess) {
if (isFindSuccess.not()) {
onFailureMsg(
msg = "trying ${remedyPlans.size} times and all failure by RemedyPlan",
throwable = lastError,
@@ -206,7 +229,7 @@ class ConstructorFinder(
* @param isNoSuch 是否没有找到构造方法 - 默认否
* @param e 错误信息
*/
inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) {
inner class Result(internal val isNoSuch: Boolean = false, private val e: Throwable? = null) {
/**
* 创建监听结果事件方法体

View File

@@ -33,8 +33,9 @@ import android.os.SystemClock
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Field
@@ -50,6 +51,9 @@ class FieldFinder(
override val classSet: Class<*>? = null
) : BaseFinder(tag = "Field", hookInstance, classSet) {
/** [ModifierRules] 实例 */
private var modifiers: ModifierRules? = null
/**
* [Field] 名称
*
@@ -64,6 +68,16 @@ class FieldFinder(
*/
var type: Class<*>? = null
/**
* [Field] 筛选条件
*
* 可不设置筛选条件 - 默认模糊查找并取第一个匹配的 [Field]
* @param initiate 方法体
*/
fun modifiers(initiate: ModifierRules.() -> Unit) {
modifiers = ModifierRules().apply(initiate)
}
/**
* 得到变量处理结果
*
@@ -80,10 +94,7 @@ class FieldFinder(
}
else -> try {
runBlocking {
memberInstance =
if (type != null)
ReflectionUtils.findFieldIfExists(classSet, type?.name, name)
else classSet?.getDeclaredField(name)?.apply { isAccessible = true }
memberInstance = ReflectionTool.findField(classSet, name, modifiers, type)
}.result { onHookLogMsg(msg = "Find Field [${memberInstance}] takes ${it}ms [${hookTag}]") }
Result()
} catch (e: Throwable) {
@@ -111,7 +122,7 @@ class FieldFinder(
* @param isNoSuch 是否没有找到变量 - 默认否
* @param e 错误信息
*/
inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) {
inner class Result(internal val isNoSuch: Boolean = false, private val e: Throwable? = null) {
/**
* 创建监听结果事件方法体

View File

@@ -35,9 +35,10 @@ package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Method
@@ -62,6 +63,9 @@ class MethodFinder(
/** [Method] 参数数组 */
private var paramTypes: Array<out Class<*>>? = null
/** [ModifierRules] 实例 */
private var modifiers: ModifierRules? = null
/**
* [Method] 名称
*
@@ -69,6 +73,15 @@ class MethodFinder(
*/
var name = ""
/**
* [Method] 参数个数
*
* 你可以不使用 [param] 指定参数类型而是仅使用此变量指定参数个数
*
* 若参数个数小于零则忽略并使用 [param]
*/
var paramCount = -1
/**
* [Method] 返回值
*
@@ -76,29 +89,38 @@ class MethodFinder(
*/
var returnType: Class<*>? = null
/**
* [Method] 筛选条件
*
* 可不设置筛选条件 - 默认模糊查找并取第一个匹配的 [Method]
* @param initiate 方法体
*/
fun modifiers(initiate: ModifierRules.() -> Unit) {
modifiers = ModifierRules().apply(initiate)
}
/**
* [Method] 参数
*
* 如果同时使用了 [paramCount] 则 [paramTypes] 的数量必须与 [paramCount] 完全匹配
*
* - ❗无参 [Method] 不要使用此方法
*
* - ❗有参 [Method] 必须使用此方法设定参数
* - ❗有参 [Method] 必须使用此方法设定参数或使用 [paramCount] 指定个数
* @param paramType 参数类型数组
*/
fun param(vararg paramType: Class<*>) {
if (paramType.isEmpty()) error("paramType is empty, please delete param() method")
if (paramType.isEmpty()) error("paramTypes is empty, please delete param() method")
paramTypes = paramType
}
/**
* 得到方法
* @return [Method]
* @throws IllegalStateException 如果 [name] 未设置
* @throws IllegalStateException 如果 [classSet] 为 null
* @throws NoSuchMethodError 如果找不到方法
*/
private val result
get() = if (paramTypes != null)
ReflectionUtils.findMethodBestMatch(classSet, returnType, name, *paramTypes!!)
else ReflectionUtils.findMethodNoParam(classSet, returnType, name)
private val result get() = ReflectionTool.findMethod(classSet, name, modifiers, returnType, paramCount, paramTypes)
/**
* 设置实例
@@ -194,7 +216,7 @@ class MethodFinder(
onFailureMsg(msg = "trying ${p + 1} times by RemedyPlan --> $it", isAlwaysPrint = true)
}
}
if (!isFindSuccess) {
if (isFindSuccess.not()) {
onFailureMsg(
msg = "trying ${remedyPlans.size} times and all failure by RemedyPlan",
throwable = lastError,
@@ -230,7 +252,7 @@ class MethodFinder(
* @param isNoSuch 是否没有找到方法 - 默认否
* @param e 错误信息
*/
inner class Result(private val isNoSuch: Boolean = false, private val e: Throwable? = null) {
inner class Result(internal val isNoSuch: Boolean = false, private val e: Throwable? = null) {
/**
* 创建监听结果事件方法体

View File

@@ -0,0 +1,194 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by fankes on 2022/3/27.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.core.finder.type
import java.lang.reflect.Member
import java.lang.reflect.Modifier
/**
* 这是一个 [Member] 描述符定义类
*
* 可对 R8 混淆后的 [Member] 进行更加详细的定位
*/
class ModifierRules {
/** 描述声明使用 */
private var isPublic = false
/** 描述声明使用 */
private var isPrivate = false
/** 描述声明使用 */
private var isProtected = false
/** 描述声明使用 */
private var isStatic = false
/** 描述声明使用 */
private var isFinal = false
/** 描述声明使用 */
private var isSynchronized = false
/** 描述声明使用 */
private var isVolatile = false
/** 描述声明使用 */
private var isTransient = false
/** 描述声明使用 */
private var isNative = false
/** 描述声明使用 */
private var isInterface = false
/** 描述声明使用 */
private var isAbstract = false
/** 描述声明使用 */
private var isStrict = false
/** 添加描述 [Member] 类型包含 public */
fun asPublic() {
isPublic = true
}
/** 添加描述 [Member] 类型包含 private */
fun asPrivate() {
isPrivate = true
}
/** 添加描述 [Member] 类型包含 protected */
fun asProtected() {
isProtected = true
}
/**
* 添加描述 [Member] 类型包含 static
*
* 对于任意的静态 [Member] 可添加此描述进行确定
*
* 特别注意 Kotlin -> Jvm 后的 object 类中的方法并不是静态的
*/
fun asStatic() {
isStatic = true
}
/**
* 添加描述 [Member] 类型包含 final
*
* 在 Kotlin -> Jvm 后没有 open 标识的 [Member] 和没有任何关联的 [Member] 都将为 final
*/
fun asFinal() {
isFinal = true
}
/** 添加描述 [Member] 类型包含 synchronized */
fun asSynchronized() {
isSynchronized = true
}
/** 添加描述 [Member] 类型包含 volatile */
fun asVolatile() {
isVolatile = true
}
/** 添加描述 [Member] 类型包含 transient */
fun asTransient() {
isTransient = true
}
/**
* 添加描述 [Member] 类型包含 native
*
* 对于任意 JNI 对接的 [Member] 可添加此描述进行确定
*/
fun asNative() {
isNative = true
}
/** 添加描述 [Member] 类型包含 interface */
fun asInterface() {
isInterface = true
}
/**
* 添加描述 [Member] 类型包含 abstract
*
* 对于任意的抽象 [Member] 可添加此描述进行确定
*/
fun asAbstract() {
isAbstract = true
}
/** 添加描述 [Member] 类型包含 strict */
fun asStrict() {
isStrict = true
}
/**
* 对比 [Member] 类型是否符合条件
* @param member 实例
* @return [Boolean] 是否符合条件
*/
internal fun contains(member: Member): Boolean {
var conditions = true
if (isPublic) conditions = Modifier.isPublic(member.modifiers)
if (isPrivate) conditions = conditions && Modifier.isPrivate(member.modifiers)
if (isProtected) conditions = conditions && Modifier.isProtected(member.modifiers)
if (isStatic) conditions = conditions && Modifier.isStatic(member.modifiers)
if (isFinal) conditions = conditions && Modifier.isFinal(member.modifiers)
if (isSynchronized) conditions = conditions && Modifier.isSynchronized(member.modifiers)
if (isVolatile) conditions = conditions && Modifier.isVolatile(member.modifiers)
if (isTransient) conditions = conditions && Modifier.isTransient(member.modifiers)
if (isNative) conditions = conditions && Modifier.isNative(member.modifiers)
if (isInterface) conditions = conditions && Modifier.isInterface(member.modifiers)
if (isAbstract) conditions = conditions && Modifier.isAbstract(member.modifiers)
if (isStrict) conditions = conditions && Modifier.isStrict(member.modifiers)
return conditions
}
override fun toString(): String {
var conditions = ""
if (isPublic) conditions += "<public> "
if (isPrivate) conditions += "<private> "
if (isProtected) conditions += "<protected> "
if (isStatic) conditions += "<static> "
if (isFinal) conditions += "<final> "
if (isSynchronized) conditions += "<synchronized> "
if (isVolatile) conditions += "<volatile> "
if (isTransient) conditions += "<transient> "
if (isNative) conditions += "<native> "
if (isInterface) conditions += "<interface> "
if (isAbstract) conditions += "<abstract> "
if (isStrict) conditions += "<strict> "
return "[${conditions.trim()}]"
}
}

View File

@@ -33,7 +33,8 @@ import com.highcapable.yukihookapi.hook.bean.HookClass
import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
import java.lang.reflect.Member
/**
* [Class] 转换为 [HookClass]
@@ -80,51 +81,31 @@ fun String.hasClass(loader: ClassLoader?) = try {
/**
* 查找变量是否存在
* @param name 名称
* @param type 类型 - 不填默认模糊
* @param initiate 方法体
* @return [Boolean] 是否存在
*/
fun Class<*>.hasField(name: String, type: Class<*>? = null): Boolean =
try {
if (type != null)
ReflectionUtils.findFieldIfExists(this, type.name, name)
else getDeclaredField(name).apply { isAccessible = true }
true
} catch (_: Throwable) {
false
}
fun Class<*>.hasField(initiate: FieldFinder.() -> Unit) = field(initiate).ignoredError().isNoSuch.not()
/**
* 查找方法是否存在
* @param name 名称
* @param paramType params
* @param returnType 返回类型 - 不填默认模糊
* @param initiate 方法体
* @return [Boolean] 是否存在
*/
fun Class<*>.hasMethod(name: String, vararg paramType: Class<*>, returnType: Class<*>? = null): Boolean =
try {
if (paramType.isNotEmpty())
ReflectionUtils.findMethodBestMatch(this, returnType, name, *paramType)
else ReflectionUtils.findMethodNoParam(this, returnType, name)
true
} catch (_: Throwable) {
false
}
fun Class<*>.hasMethod(initiate: MethodFinder.() -> Unit) = method(initiate).ignoredError().isNoSuch.not()
/**
* 查找构造方法是否存在
* @param paramType params
* @param initiate 方法体
* @return [Boolean] 是否存在
*/
fun Class<*>.hasConstructor(vararg paramType: Class<*>): Boolean =
try {
if (paramType.isNotEmpty())
ReflectionUtils.findConstructorExact(this, *paramType)
else ReflectionUtils.findConstructorExact(this)
true
} catch (_: Throwable) {
false
}
fun Class<*>.hasConstructor(initiate: ConstructorFinder.() -> Unit) = constructor(initiate).ignoredError().isNoSuch.not()
/**
* 查询 [Member] 中匹配的描述符
* @param initiate 方法体
* @return [Boolean] 是否存在
*/
fun Member.hasModifiers(initiate: ModifierRules.() -> Unit) = ModifierRules().apply(initiate).contains(this)
/**
* 查找并得到变量
@@ -145,4 +126,4 @@ fun Class<*>.method(initiate: MethodFinder.() -> Unit) = MethodFinder(classSet =
* @param initiate 查找方法体
* @return [ConstructorFinder.Result]
*/
fun Class<*>.constructor(initiate: ConstructorFinder.() -> Unit) = ConstructorFinder(classSet = this).apply(initiate).build()
fun Class<*>.constructor(initiate: ConstructorFinder.() -> Unit) = ConstructorFinder(classSet = this).apply(initiate).build()

View File

@@ -41,7 +41,7 @@ import de.robv.android.xposed.XposedBridge
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerD(msg: String, isDisableLog: Boolean = false) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (!isDisableLog) loggerD(msg = msg)
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (isDisableLog.not()) loggerD(msg = msg)
}
/**
@@ -52,7 +52,7 @@ internal fun yLoggerD(msg: String, isDisableLog: Boolean = false) {
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerI(msg: String, isDisableLog: Boolean = false) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (!isDisableLog) loggerI(msg = msg)
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (isDisableLog.not()) loggerI(msg = msg)
}
/**
@@ -63,7 +63,7 @@ internal fun yLoggerI(msg: String, isDisableLog: Boolean = false) {
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerW(msg: String, isDisableLog: Boolean = false) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (!isDisableLog) loggerW(msg = msg)
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (isDisableLog.not()) loggerW(msg = msg)
}
/**
@@ -75,7 +75,7 @@ internal fun yLoggerW(msg: String, isDisableLog: Boolean = false) {
* @param isDisableLog 禁止打印日志 - 标识后将什么也不做 - 默认为 false
*/
internal fun yLoggerE(msg: String, e: Throwable? = null, isDisableLog: Boolean = false) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (!isDisableLog) loggerE(msg = msg, e = e)
if (YukiHookAPI.Configs.isAllowPrintingLogs) if (isDisableLog.not()) loggerE(msg = msg, e = e)
}
/**

View File

@@ -0,0 +1,195 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by fankes on 2022/3/27.
*/
package com.highcapable.yukihookapi.hook.utils
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Member
import java.lang.reflect.Method
/**
* 这是一个对 [Member] 查找的工具实现类
*/
internal object ReflectionTool {
/** 当前工具类的标签 */
private const val TAG = "YukiHookAPI#ReflectionTool"
/**
* 查找任意变量
* @param classSet 变量所在类
* @param name 变量名称
* @param modifiers 变量描述
* @param type 变量类型
* @return [Field]
* @throws IllegalStateException 如果 [classSet] 为 null
* @throws NoSuchFieldError 如果找不到变量
*/
internal fun findField(classSet: Class<*>?, name: String, modifiers: ModifierRules?, type: Class<*>?): Field {
var field: Field? = null
run {
classSet?.declaredFields?.forEach {
var conditions = name == it.name
if (type != null) conditions = conditions && it.type == type
if (modifiers != null) conditions = conditions && modifiers.contains(it)
if (conditions) {
field = it.apply { isAccessible = true }
return@run
}
} ?: error("Can't find this Field [$name] because classSet is null")
}
return field ?: throw NoSuchFieldError(
"Can't find this Field --> " +
"name:[$name] " +
"type:[$type] " +
"modifiers:${modifiers ?: "[]"} " +
"in Class [$classSet] " +
"by $TAG"
)
}
/**
* 查找任意方法
* @param classSet 方法所在类
* @param name 方法名称
* @param modifiers 方法描述
* @param returnType 方法返回值
* @param paramCount 方法参数个数
* @param paramTypes 方法参数类型
* @return [Method]
* @throws IllegalStateException 如果 [classSet] 为 null
* @throws NoSuchMethodError 如果找不到方法
*/
internal fun findMethod(
classSet: Class<*>?,
name: String,
modifiers: ModifierRules?,
returnType: Class<*>?,
paramCount: Int,
paramTypes: Array<out Class<*>>?
): Method {
var method: Method? = null
run {
classSet?.declaredMethods?.forEach {
var conditions = name == it.name
if (returnType != null) conditions = conditions && it.returnType == returnType
if (paramCount >= 0) conditions = conditions && it.parameterTypes.size == paramCount
if (paramTypes != null) conditions = conditions && arrayContentsEq(paramTypes, it.parameterTypes)
if (modifiers != null) conditions = conditions && modifiers.contains(it)
if (conditions) {
method = it.apply { isAccessible = true }
return@run
}
} ?: error("Can't find this Method [$name] because classSet is null")
}
return method ?: throw NoSuchMethodError(
"Can't find this Method --> " +
"name:[$name] " +
"paramCount:[${paramCount.takeIf { it >= 0 } ?: "unspecified"}] " +
"paramTypes:[${paramTypes.typeOfString()}] " +
"returnType:[$returnType] " +
"modifiers:${modifiers ?: "[]"} " +
"in Class [$classSet] " +
"by $TAG"
)
}
/**
* 查找任意构造方法
* @param classSet 构造方法所在类
* @param modifiers 构造方法描述
* @param paramCount 构造方法参数个数
* @param paramTypes 构造方法参数类型
* @return [Constructor]
* @throws IllegalStateException 如果 [classSet] 为 null
* @throws NoSuchMethodError 如果找不到构造方法
*/
internal fun findConstructor(
classSet: Class<*>?,
modifiers: ModifierRules?,
paramCount: Int,
paramTypes: Array<out Class<*>>?
): Constructor<*> {
var constructor: Constructor<*>? = null
run {
classSet?.declaredConstructors?.forEach {
var conditions = false
if (paramCount >= 0) conditions = it.parameterTypes.size == paramCount
if (paramTypes != null) conditions = arrayContentsEq(paramTypes, it.parameterTypes)
if (modifiers != null) conditions = conditions && modifiers.contains(it)
if (conditions) {
constructor = it.apply { isAccessible = true }
return@run
}
} ?: error("Can't find this Constructor because classSet is null")
}
return constructor ?: throw NoSuchMethodError(
"Can't find this Constructor --> " +
"paramCount:[${paramCount.takeIf { it >= 0 } ?: "unspecified"}] " +
"paramTypes:[${paramTypes.typeOfString()}] " +
"modifiers:${modifiers ?: "[]"} " +
"in Class [$classSet] " +
"by $TAG"
)
}
/**
* 获取参数数组文本化内容
* @return [String]
*/
private fun Array<out Class<*>>?.typeOfString() =
StringBuilder("(").also { sb ->
var isFirst = true
if (this == null || isEmpty()) return "()"
forEach {
if (isFirst) isFirst = false else sb.append(",")
sb.append(it.canonicalName)
}
sb.append(")")
}.toString()
/**
* 判断两个数组是否相等
*
* 复制自 [Class] 中的 [Class.arrayContentsEq]
* @param fArray 第一个数组
* @param lArray 第二个数组
* @return [Boolean] 是否相等
*/
private fun arrayContentsEq(fArray: Array<out Any>?, lArray: Array<out Any>?) = run {
if (fArray != null) when {
lArray == null -> fArray.isEmpty()
fArray.size != lArray.size -> false
else -> {
for (i in fArray.indices) if (fArray[i] !== lArray[i]) return@run false
true
}
} else lArray == null || lArray.isEmpty()
}
}

View File

@@ -1,210 +0,0 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by zpp0196 on 2019/1/24 0024.
* This file is Modified by fankes on 2022/2/2 2240.
*/
package com.highcapable.yukihookapi.hook.utils;
import android.text.TextUtils;
import com.highcapable.yukihookapi.annotation.DoNotUseClass;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@SuppressWarnings("ALL")
@DoNotUseClass
/**
* ReflectionUtils
*/
public class ReflectionUtils {
private static final HashMap<String, Field> fieldCache = new HashMap<>();
private static final HashMap<String, Method> methodCache = new HashMap<>();
private static String getParametersString(Class<?>... clazzes) {
StringBuilder sb = new StringBuilder("(");
boolean first = true;
for (Class<?> clazz : clazzes) {
if (first)
first = false;
else
sb.append(",");
if (clazz != null)
sb.append(clazz.getCanonicalName());
else
sb.append("null");
}
sb.append(")");
return sb.toString();
}
/**
* 适用于查找混淆类型的 abcd 变量
*
* @param clazz 变量所在类
* @param typeName 类型名称
* @param fieldName 变量名
* @return Field
* @throws NoSuchFieldException 如果找不到变量
*/
public static Field findFieldIfExists(Class<?> clazz, String typeName, String fieldName) throws NoSuchFieldException {
String fullFieldName = "name:[" + fieldName + "] type:[" + typeName + "] in Class [" + clazz.getName() + "] by YukiHookAPI#finder";
if (!fieldCache.containsKey(fullFieldName)) {
if (clazz != null && !TextUtils.isEmpty(typeName) && !TextUtils.isEmpty(fieldName)) {
Class<?> clz = clazz;
do {
for (Field field : clz.getDeclaredFields()) {
if (field.getType()
.getName()
.equals(typeName) && field.getName()
.equals(fieldName)) {
field.setAccessible(true);
fieldCache.put(fullFieldName, field);
return field;
}
}
} while ((clz = clz.getSuperclass()) != null);
throw new NoSuchFieldException("Can't find this field --> " + fullFieldName);
}
return null;
} else {
Field field = fieldCache.get(fullFieldName);
if (field == null)
throw new NoSuchFieldError(fullFieldName);
return field;
}
}
/**
* 适用于查找混淆类型的 abcd 方法 - 无 param
*
* @param clazz 方法所在类
* @param returnType 返回类型
* @param methodName 方法名
* @return Method
* @throws NoSuchMethodError 如果找不到方法
*/
public static Method findMethodNoParam(Class<?> clazz, Class<?> returnType, String methodName) {
String fullMethodName = "name:[" + methodName + "] in Class [" + clazz.getName() + "] by YukiHookAPI#finder";
if (!methodCache.containsKey(fullMethodName)) {
Method method = findMethodIfExists(clazz, returnType, methodName);
methodCache.put(fullMethodName, method);
return method;
} else {
return methodCache.get(fullMethodName);
}
}
/**
* 不区分 param 整个类搜索 - 适用于混淆方法 abcd
*
* @param clazz 方法所在类
* @param returnType 返回类型
* @param methodName 方法名
* @param parameterTypes 方法参数类型数组
* @return Method
* @throws NoSuchMethodError 如果找不到方法
*/
public static Method findMethodBestMatch(Class<?> clazz, Class<?> returnType, String methodName, Class<?>... parameterTypes) {
String fullMethodName = "name:[" + methodName + "] paramType:[" + getParametersString(parameterTypes) + "] in Class [" + clazz.getName() + "] by YukiHookAPI#finder";
if (!methodCache.containsKey(fullMethodName)) {
Method method = findMethodIfExists(clazz, returnType, methodName, parameterTypes);
methodCache.put(fullMethodName, method);
return method;
} else {
return methodCache.get(fullMethodName);
}
}
/**
* 查找构造方法
*
* @param clazz 构造类所在类
* @param parameterTypes 构造类方法参数类型数组
* @return Constructor
* @throws NoSuchMethodError 如果找不到构造类
*/
public static Constructor<?> findConstructorExact(Class<?> clazz, Class<?>... parameterTypes) {
String fullConstructorName = "paramType:[" + getParametersString(parameterTypes) + "in Class [" + clazz.getName() + "] by YukiHookAPI#finder";
try {
Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError("Can't find this constructor --> " + fullConstructorName);
}
}
private static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
String fullMethodName = "name:[" + methodName + "] paramType:[" + getParametersString(parameterTypes) + "] in Class [" + clazz.getName() + "] by YukiHookAPI#finder";
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError("Can't find this method --> " + fullMethodName);
}
}
private static Method findMethodIfExists(Class<?> clazz, Class<?> returnType, String methodName, Class<?>... parameterTypes) {
long l = System.currentTimeMillis();
if (clazz != null && !TextUtils.isEmpty(methodName)) {
Class<?> clz = clazz;
if (returnType == null) return findMethodExact(clazz, methodName, parameterTypes);
do {
Method[] methods = findMethodsByExactParameters(clazz, returnType, parameterTypes);
for (Method method : methods) if (method.getName().equals(methodName)) return method;
} while ((clz = clz.getSuperclass()) != null);
}
throw new IllegalArgumentException("Can't find this method --> name:[" + methodName + "] returnType:[" + returnType.getName() + "] paramType:[" + getParametersString(parameterTypes) + "] in Class [" + clazz.getName() + "] by YukiHookAPI#finder");
}
private static Method[] findMethodsByExactParameters(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes) {
List<Method> result = new LinkedList<Method>();
for (Method method : clazz.getDeclaredMethods()) {
if (returnType != null && returnType != method.getReturnType()) continue;
Class<?>[] methodParameterTypes = method.getParameterTypes();
if (parameterTypes.length != methodParameterTypes.length) continue;
boolean match = true;
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i] != methodParameterTypes[i]) {
match = false;
break;
}
}
if (!match) continue;
method.setAccessible(true);
result.add(method);
}
return result.toArray(new Method[result.size()]);
}
}