diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt index dcb2b409..31c3302e 100644 --- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt +++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt @@ -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() } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index bcfdadc9..52ba8b79 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -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") } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt index f0565dbe..8623ec0d 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt @@ -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) diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt index 58100650..b4442606 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt @@ -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>? = 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) { /** * 创建监听结果事件方法体 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt index c92fc3ee..9b97a638 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt @@ -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) { /** * 创建监听结果事件方法体 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt index 38d1aa9f..3dac9686 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt @@ -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>? = 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) { /** * 创建监听结果事件方法体 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/type/ModifierRules.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/type/ModifierRules.kt new file mode 100644 index 00000000..d4c603b5 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/type/ModifierRules.kt @@ -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 += " " + if (isPrivate) conditions += " " + if (isProtected) conditions += " " + if (isStatic) conditions += " " + if (isFinal) conditions += " " + if (isSynchronized) conditions += " " + if (isVolatile) conditions += " " + if (isTransient) conditions += " " + if (isNative) conditions += " " + if (isInterface) conditions += " " + if (isAbstract) conditions += " " + if (isStrict) conditions += " " + return "[${conditions.trim()}]" + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt index 8fbf2d0d..54c3409e 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/ReflectionFactory.kt @@ -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() \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt index bf4d7f3a..8a6e8c5c 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/log/LoggerFactory.kt @@ -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) } /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionTool.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionTool.kt new file mode 100644 index 00000000..5c7e2f74 --- /dev/null +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionTool.kt @@ -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>? + ): 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>? + ): 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>?.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?, lArray: Array?) = 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() + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java deleted file mode 100644 index ff242019..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/utils/ReflectionUtils.java +++ /dev/null @@ -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 fieldCache = new HashMap<>(); - private static final HashMap 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 result = new LinkedList(); - 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()]); - } -}