Added DexClassFinder function allowed ClassLoader to search classes (Beta Feature)

This commit is contained in:
2022-09-21 14:30:17 +08:00
parent 6ea012472d
commit 41546b2c9f
23 changed files with 3025 additions and 8 deletions

View File

@@ -0,0 +1,75 @@
/*
* 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/9/4.
*/
package com.highcapable.yukihookapi.hook.core.finder.base
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.yLoggerI
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
/**
* 这是 [Class] 查找类功能的基本类实现
* @param loaderSet 当前使用的 [ClassLoader] 实例
*/
abstract class ClassBaseFinder internal constructor(internal open val loaderSet: ClassLoader? = null) : BaseFinder() {
/** 当前找到的 [Class] 数组 */
internal var classInstances = HashSet<Class<*>>()
/** 是否开启忽略错误警告功能 */
internal var isShutErrorPrinting = false
/**
* 将目标类型转换为可识别的兼容类型
* @param any 当前需要转换的实例
* @param tag 当前查找类的标识
* @return [Class] or null
*/
internal fun compatType(any: Any?, tag: String) = any?.compat(tag, loaderSet)
/**
* 在开启 [YukiHookAPI.Configs.isDebug] 且在 [YukiHookBridge.hasXposedBridge] 情况下输出调试信息
* @param msg 调试日志内容
*/
internal fun onDebuggingMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug && YukiHookBridge.hasXposedBridge) yLoggerI(msg = msg)
}
/**
* 发生错误时输出日志
* @param throwable 错误
*/
internal fun onFailureMsg(throwable: Throwable? = null) {
if (isShutErrorPrinting) return
yLoggerE(msg = "NoClassDefFound happend in [$loaderSet]", e = throwable)
}
@YukiPrivateApi
override fun failure(throwable: Throwable?) = error("DexClassFinder does not contain this usage")
}

View File

@@ -0,0 +1,620 @@
/*
* 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/9/4.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder.classes
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.SystemClock
import androidx.core.content.pm.PackageInfoCompat
import com.highcapable.yukihookapi.annotation.YukiPrivateApi
import com.highcapable.yukihookapi.hook.core.finder.base.ClassBaseFinder
import com.highcapable.yukihookapi.hook.core.finder.classes.data.ClassRulesData
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.ConstructorRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.FieldRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.MemberRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.MethodRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.base.BaseRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.result.MemberRulesResult
import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.searchClass
import com.highcapable.yukihookapi.hook.factory.toClass
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.utils.await
import com.highcapable.yukihookapi.hook.utils.runBlocking
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
import dalvik.system.BaseDexClassLoader
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Member
import java.lang.reflect.Method
/**
* [Class] 查找类
*
* 可使用 [BaseDexClassLoader] 通过指定条件查找指定 [Class] 或一组 [Class]
*
* - ❗此功能尚在试验阶段 - 性能与稳定性可能仍然存在问题 - 使用过程遇到问题请向我们报告并帮助我们改进
* @param name 标识当前 [Class] 缓存的名称 - 不设置将不启用缓存 - 启用缓存必须启用 [async]
* @param async 是否启用异步
* @param loaderSet 当前使用的 [ClassLoader] 实例
*/
class DexClassFinder @PublishedApi internal constructor(
internal var name: String,
internal var async: Boolean,
override val loaderSet: ClassLoader?
) : ClassBaseFinder(loaderSet) {
companion object {
/** 缓存的存储文件名 */
private const val CACHE_FILE_NAME = "config_yukihook_cache_obfuscate_classes"
/**
* 通过 [Context] 获取当前 [SharedPreferences]
* @param versionName 版本名称 - 默认空
* @param versionCode 版本号 - 默认空
* @return [SharedPreferences]
*/
private fun Context.currentSp(versionName: String? = null, versionCode: Long? = null) =
getSharedPreferences(packageManager?.getPackageInfo(packageName, PackageManager.GET_META_DATA)
?.let { "${CACHE_FILE_NAME}_${versionName ?: it.versionName}_${versionCode ?: PackageInfoCompat.getLongVersionCode(it)}" }
?: "${CACHE_FILE_NAME}_unknown",
Context.MODE_PRIVATE)
/**
* 清除当前 [DexClassFinder] 的 [Class] 缓存
*
* 适用于全部通过 [ClassLoader.searchClass] or [PackageParam.searchClass] 获取的 [DexClassFinder]
* @param context 当前 [Context] - 不填默认获取 [YukiHookAppHelper.currentApplication]
* @param versionName 版本名称 - 默认空
* @param versionCode 版本号 - 默认空
*/
fun clearCache(context: Context? = YukiHookAppHelper.currentApplication(), versionName: String? = null, versionCode: Long? = null) {
context?.currentSp(versionName, versionCode)?.edit()?.clear()?.apply()
?: yLoggerW(msg = "Cannot clear cache for DexClassFinder because got null context instance")
}
}
@PublishedApi
override var rulesData = ClassRulesData()
/**
* 设置 [Class] 完整名称
*
* 只会查询匹配到的 [Class.getName]
*
* 例如 com.demo.Test 需要填写 com.demo.Test
* @return [String]
*/
var fullName
get() = rulesData.fullName?.name ?: ""
set(value) {
rulesData.fullName = rulesData.createNameRulesData(value)
}
/**
* 设置 [Class] 简单名称
*
* 只会查询匹配到的 [Class.getSimpleName]
*
* 例如 com.demo.Test 只需要填写 Test
*
* 对于匿名类例如 com.demo.Test$InnerTest 会为空 - 此时你可以使用 [singleName]
* @return [String]
*/
var simpleName
get() = rulesData.simpleName?.name ?: ""
set(value) {
rulesData.simpleName = rulesData.createNameRulesData(value)
}
/**
* 设置 [Class] 独立名称
*
* 设置后将首先使用 [Class.getSimpleName] - 若为空则会使用 [Class.getName] 进行处理
*
* 例如 com.demo.Test 只需要填写 Test
*
* 对于匿名类例如 com.demo.Test$InnerTest 只需要填写 Test$InnerTest
* @return [String]
*/
var singleName
get() = rulesData.singleName?.name ?: ""
set(value) {
rulesData.singleName = rulesData.createNameRulesData(value)
}
/**
* 设置在指定包名范围查询当前 [Class]
*
* 设置后仅会在当前 [name] 开头匹配的包名路径下进行查询 - 可提升查询速度
*
* 例如 ↓
*
* com.demo.test
*
* com.demo.test.demo
*
* - ❗建议设置此参数指定查询范围 - 否则 [Class] 过多时将会非常慢
* @param name 指定包名
* @return [FromPackageRules] 可设置 [FromPackageRules.absolute] 标识包名绝对匹配
*/
fun from(vararg name: String) = FromPackageRules(arrayListOf<ClassRulesData.PackageRulesData>().also {
name.takeIf { e -> e.isNotEmpty() }?.forEach { e -> it.add(rulesData.createPackageRulesData(e)) }
if (it.isNotEmpty()) rulesData.fromPackages.addAll(it)
})
/**
* 设置 [Class] 标识符筛选条件
*
* - 可不设置筛选条件
* @param conditions 条件方法体
*/
fun modifiers(conditions: ModifierConditions) {
rulesData.modifiers = conditions
}
/**
* 设置 [Class] 完整名称
*
* 只会查询匹配到的 [Class.getName]
*
* 例如 com.demo.Test 需要填写 com.demo.Test
* @param value 名称
* @return [ClassNameRules] 可设置 [ClassNameRules.optional] 标识类名可选
*/
fun fullName(value: String) = rulesData.createNameRulesData(value).let {
rulesData.fullName = it
ClassNameRules(it)
}
/**
* 设置 [Class] 简单名称
*
* 只会查询匹配到的 [Class.getSimpleName]
*
* 例如 com.demo.Test 只需要填写 Test
*
* 对于匿名类例如 com.demo.Test$InnerTest 会为空 - 此时你可以使用 [singleName]
* @param value 名称
* @return [ClassNameRules] 可设置 [ClassNameRules.optional] 标识类名可选
*/
fun simpleName(value: String) = rulesData.createNameRulesData(value).let {
rulesData.simpleName = it
ClassNameRules(it)
}
/**
* 设置 [Class] 独立名称
*
* 设置后将首先使用 [Class.getSimpleName] - 若为空则会使用 [Class.getName] 进行处理
*
* 例如 com.demo.Test 只需要填写 Test
*
* 对于匿名类例如 com.demo.Test$InnerTest 只需要填写 Test$InnerTest
* @param value 名称
* @return [ClassNameRules] 可设置 [ClassNameRules.optional] 标识类名可选
*/
fun singleName(value: String) = rulesData.createNameRulesData(value).let {
rulesData.singleName = it
ClassNameRules(it)
}
/**
* 设置 [Class] 完整名称条件
*
* 只会查询匹配到的 [Class.getName]
* @param conditions 条件方法体
*/
fun fullName(conditions: NameConditions) {
rulesData.fullNameConditions = conditions
}
/**
* 设置 [Class] 简单名称条件
*
* 只会查询匹配到的 [Class.getSimpleName]
* @param conditions 条件方法体
*/
fun simpleName(conditions: NameConditions) {
rulesData.simpleNameConditions = conditions
}
/**
* 设置 [Class] 独立名称条件
*
* 设置后将首先使用 [Class.getSimpleName] - 若为空则会使用 [Class.getName] 进行处理
* @param conditions 条件方法体
*/
fun singleName(conditions: NameConditions) {
rulesData.singleNameConditions = conditions
}
/** 设置 [Class] 继承的父类 */
inline fun <reified T> extends() {
rulesData.extendsClass.add(T::class.java.name)
}
/**
* 设置 [Class] 继承的父类
*
* 会同时查询 [name] 中所有匹配的父类
* @param name [Class] 完整名称
*/
fun extends(vararg name: String) {
rulesData.extendsClass.addAll(name.toList())
}
/** 设置 [Class] 实现的接口类 */
inline fun <reified T> implements() {
rulesData.implementsClass.add(T::class.java.name)
}
/**
* 设置 [Class] 实现的接口类
*
* 会同时查询 [name] 中所有匹配的接口类
* @param name [Class] 完整名称
*/
fun implements(vararg name: String) {
rulesData.implementsClass.addAll(name.toList())
}
/**
* 标识 [Class] 为匿名类
*
* 例如 com.demo.Test$1 或 com.demo.Test$InnerTest
*
* 标识后你可以使用 [enclosing] 来进一步指定匿名类的 (封闭类) 主类
*/
fun anonymous() {
rulesData.isAnonymousClass = true
}
/**
* 设置 [Class] 没有任何继承
*
* 此时 [Class] 只应该继承于 [Any]
*
* - ❗设置此条件后 [extends] 将失效
*/
fun noExtends() {
rulesData.isNoExtendsClass = true
}
/**
* 设置 [Class] 没有任何接口
*
* - ❗设置此条件后 [implements] 将失效
*/
fun noImplements() {
rulesData.isNoImplementsClass = true
}
/**
* 设置 [Class] 没有任何继承与接口
*
* 此时 [Class] 只应该继承于 [Any]
*
* - ❗设置此条件后 [extends] 与 [implements] 将失效
*/
fun noSuper() {
noExtends()
noImplements()
}
/** 设置 [Class] 匿名类的 (封闭类) 主类 */
inline fun <reified T> enclosing() {
rulesData.enclosingClass.add(T::class.java.name)
}
/**
* 设置 [Class] 匿名类的 (封闭类) 主类
*
* 会同时查询 [name] 中所有匹配的 (封闭类) 主类
* @param name [Class] 完整名称
*/
fun enclosing(vararg name: String) {
rulesData.enclosingClass.addAll(name.toList())
}
/**
* 包名范围名称过滤匹配条件实现类
* @param packages 包名数组
*/
inner class FromPackageRules internal constructor(private val packages: ArrayList<ClassRulesData.PackageRulesData>) {
/**
* 设置包名绝对匹配
*
* 例如有如下包名 ↓
*
* com.demo.test.a
*
* com.demo.test.a.b
*
* com.demo.test.active
*
* 若包名条件为 "com.demo.test.a" 则绝对匹配仅能匹配到第一个
*
* 相反地 - 不设置以上示例会全部匹配
*/
fun absolute() = packages.takeIf { it.isNotEmpty() }?.forEach { it.isAbsolute = true }
}
/**
* 类名匹配条件实现类
* @param name 类名匹配实例
*/
inner class ClassNameRules internal constructor(private val name: ClassRulesData.NameRulesData) {
/**
* 设置类名可选
*
* 例如有如下类名 ↓
*
* com.demo.Test (fullName) / Test (simpleName)
*
* defpackage.a (fullName) / a (simpleName)
*
* 这两个类名都是同一个类 - 但是在有些版本中被混淆有些版本没有
*
* 此时可设置类名为 "com.demo.Test" (fullName) / "Test" (simpleName)
*
* 这样就可在完全匹配类名情况下使用类名而忽略其它查询条件 - 否则忽略此条件继续使用其它查询条件
*/
fun optional() {
name.isOptional = true
}
}
/**
* 设置 [Class] 满足的 [Member] 条件
* @param initiate 条件方法体
* @return [MemberRulesResult]
*/
inline fun member(initiate: MemberRules.() -> Unit = {}) = BaseRules.createMemberRules(this).apply(initiate).build()
/**
* 设置 [Class] 满足的 [Field] 条件
* @param initiate 条件方法体
* @return [MemberRulesResult]
*/
inline fun field(initiate: FieldRules.() -> Unit = {}) = BaseRules.createFieldRules(this).apply(initiate).build()
/**
* 设置 [Class] 满足的 [Method] 条件
* @param initiate 条件方法体
* @return [MemberRulesResult]
*/
inline fun method(initiate: MethodRules.() -> Unit = {}) = BaseRules.createMethodRules(this).apply(initiate).build()
/**
* 设置 [Class] 满足的 [Constructor] 条件
* @param initiate 查找方法体
* @return [MemberRulesResult]
*/
inline fun constructor(initiate: ConstructorRules.() -> Unit = {}) = BaseRules.createConstructorRules(this).apply(initiate).build()
/**
* 得到 [Class] 或一组 [Class]
* @return [HashSet]<[Class]>
* @throws NoClassDefFoundError 如果找不到 [Class]
*/
private val result get() = ReflectionTool.findClasses(loaderSet, rulesData)
/**
* 从本地缓存读取 [Class] 数据
* @return [HashSet]<[Class]>
*/
private fun readFromCache(): HashSet<Class<*>> =
if (async && name.isNotBlank()) YukiHookAppHelper.currentApplication()?.let {
hashSetOf<Class<*>>().also { classes ->
it.currentSp().getStringSet(name, emptySet())?.takeIf { it.isNotEmpty() }
?.forEach { className -> if (className.hasClass(loaderSet)) classes.add(className.toClass(loaderSet)) }
}
} ?: let { SystemClock.sleep(1); readFromCache() } else hashSetOf()
/**
* 将当前 [Class] 数组名称保存到本地缓存
* @throws IllegalStateException 如果当前包名为 "android"
*/
private fun HashSet<Class<*>>.saveToCache() {
if (name.isNotBlank() && isNotEmpty()) hashSetOf<String>().also { names ->
takeIf { it.isNotEmpty() }?.forEach { names.add(it.name) }
YukiHookAppHelper.currentApplication()?.also {
if (it.packageName == "android") error("Cannot create classes cache for \"android\", please remove \"name\" param")
it.currentSp().edit().apply { putStringSet(name, names) }.apply()
}
}
}
/**
* 设置实例
* @param classes 当前找到的 [Class] 数组
*/
private fun setInstance(classes: HashSet<Class<*>>) {
classInstances.clear()
classes.takeIf { it.isNotEmpty() }?.forEach { classInstances.add(it) }
}
@YukiPrivateApi
override fun build() = runCatching {
if (loaderSet != null) {
/** 开始任务 */
fun startProcess() {
runBlocking {
setInstance(readFromCache().takeIf { it.isNotEmpty() } ?: result)
}.result { ms -> classInstances.takeIf { it.isNotEmpty() }?.forEach { onDebuggingMsg(msg = "Find Class [$it] takes ${ms}ms") } }
}
Result().also { e ->
if (async) e.await {
runCatching {
startProcess()
it.waitResultCallback?.invoke(it.get())
it.waitAllResultCallback?.invoke(it.all())
classInstances.saveToCache()
}.onFailure { e ->
it.isNotFound = true
it.throwable = e
it.noClassDefFoundErrorCallback?.invoke()
onFailureMsg(throwable = e)
}
} else startProcess()
}
} else Result(isNotFound = true, Throwable("loaderSet is null")).await { onFailureMsg() }
}.getOrElse { e -> Result(isNotFound = true, e).await { onFailureMsg(throwable = e) } }
/**
* [Class] 查找结果实现类
* @param isNotFound 是否没有找到 [Class] - 默认否
* @param throwable 错误信息
*/
inner class Result internal constructor(
@PublishedApi internal var isNotFound: Boolean = false,
@PublishedApi internal var throwable: Throwable? = null
) : BaseResult {
/** 异步方法体回调结果 */
internal var waitResultCallback: ((Class<*>?) -> Unit)? = null
/** 异步方法体回调数组结果 */
internal var waitAllResultCallback: ((HashSet<Class<*>>) -> Unit)? = null
/** 异常结果重新回调方法体 */
internal var noClassDefFoundErrorCallback: (() -> Unit)? = null
/**
* 创建监听结果事件方法体
* @param initiate 方法体
* @return [Result] 可继续向下监听
*/
inline fun result(initiate: Result.() -> Unit) = apply(initiate)
/**
* 得到 [Class] 本身
*
* - 若有多个 [Class] 结果只会返回第一个
*
* - 在查询条件找不到任何结果的时候将返回 null
*
* - ❗若你设置了 [async] 请使用 [wait] 方法
* @return [Class] or null
*/
fun get() = all().takeIf { it.isNotEmpty() }?.first()
/**
* 得到 [Class] 本身数组
*
* - 返回全部查询条件匹配的多个 [Class] 实例
*
* - 在查询条件找不到任何结果的时候将返回空的 [HashSet]
*
* - ❗若你设置了 [async] 请使用 [waitAll] 方法
* @return [HashSet]<[Class]>
*/
fun all() = classInstances
/**
* 得到 [Class] 本身数组 (依次遍历)
*
* - 回调全部查询条件匹配的多个 [Class] 实例
*
* - 在查询条件找不到任何结果的时候将不会执行
*
* - ❗若你设置了 [async] 请使用 [waitAll] 方法
* @param result 回调每个结果
* @return [Result] 可继续向下监听
*/
fun all(result: (Class<*>) -> Unit): Result {
all().takeIf { it.isNotEmpty() }?.forEach(result)
return this
}
/**
* 得到 [Class] 本身 (异步)
*
* - 若有多个 [Class] 结果只会回调第一个
*
* - 在查询条件找不到任何结果的时候将回调 null
*
* - ❗你需要设置 [async] 后此方法才会被回调 - 否则请使用 [get] 方法
* @param result 回调 - ([Class] or null)
* @return [Result] 可继续向下监听
*/
fun wait(result: (Class<*>?) -> Unit): Result {
waitResultCallback = result
return this
}
/**
* 得到 [Class] 本身数组 (异步)
*
* - 回调全部查询条件匹配的多个 [Class] 实例
*
* - 在查询条件找不到任何结果的时候将回调空的 [HashSet]
*
* - ❗你需要设置 [async] 后此方法才会被回调 - 否则请使用 [all] 方法
* @param result 回调 - ([HashSet]<[Class]>)
* @return [Result] 可继续向下监听
*/
fun waitAll(result: (HashSet<Class<*>>) -> Unit): Result {
waitAllResultCallback = result
return this
}
/**
* 监听找不到 [Class] 时
* @param result 回调错误
* @return [Result] 可继续向下监听
*/
fun onNoClassDefFoundError(result: (Throwable) -> Unit): Result {
noClassDefFoundErrorCallback = { if (isNotFound) result(throwable ?: Throwable("Initialization Error")) }
noClassDefFoundErrorCallback?.invoke()
return this
}
/**
* 忽略异常并停止打印任何错误日志
*
* - ❗此时若要监听异常结果 - 你需要手动实现 [onNoClassDefFoundError] 方法
* @return [Result] 可继续向下监听
*/
fun ignored(): Result {
isShutErrorPrinting = true
return this
}
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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/9/5.
*/
@file:Suppress("PropertyName")
package com.highcapable.yukihookapi.hook.core.finder.classes.data
import com.highcapable.yukihookapi.hook.core.finder.base.data.BaseRulesData
import com.highcapable.yukihookapi.hook.core.finder.base.rules.ModifierRules
import com.highcapable.yukihookapi.hook.core.finder.members.data.ConstructorRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.FieldRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Member
import java.lang.reflect.Method
/**
* [Class] 规则查询数据类
* @param fromPackages 指定包名范围名称数组
* @param fullName 完整名称
* @param simpleName 简单名称
* @param singleName 独立名称
* @param fullNameConditions 完整名称规则
* @param simpleNameConditions 简单名称规则
* @param singleNameConditions 独立名称规则
* @param isAnonymousClass 匿名类
* @param isNoExtendsClass 无继承的父类
* @param isNoImplementsClass 无继承的实现的接口类
* @param extendsClass 继承的父类名称数组
* @param implementsClass 实现的接口类名称数组
* @param enclosingClass 包含的封闭类 (主类) 名称数组
* @param memberRules [Member] 查询条件数据数组
* @param fieldRules [Field] 查询条件数据数组
* @param methodRules [Method] 查询条件数据数组
* @param constroctorRules [Constructor] 查询条件数据数组
*/
@PublishedApi
internal class ClassRulesData internal constructor(
var fromPackages: ArrayList<PackageRulesData> = arrayListOf(),
var fullName: NameRulesData? = null,
var simpleName: NameRulesData? = null,
var singleName: NameRulesData? = null,
var fullNameConditions: NameConditions? = null,
var simpleNameConditions: NameConditions? = null,
var singleNameConditions: NameConditions? = null,
var isAnonymousClass: Boolean? = null,
var isNoExtendsClass: Boolean? = null,
var isNoImplementsClass: Boolean? = null,
var extendsClass: ArrayList<String> = arrayListOf(),
var implementsClass: ArrayList<String> = arrayListOf(),
var enclosingClass: ArrayList<String> = arrayListOf(),
var memberRules: ArrayList<MemberRulesData> = arrayListOf(),
var fieldRules: ArrayList<FieldRulesData> = arrayListOf(),
var methodRules: ArrayList<MethodRulesData> = arrayListOf(),
var constroctorRules: ArrayList<ConstructorRulesData> = arrayListOf()
) : BaseRulesData() {
/**
* 创建类名匹配条件查询数据类
* @param name 包名
* @return [NameRulesData]
*/
internal fun createNameRulesData(name: String) = NameRulesData(name)
/**
* 创建包名范围名称过滤匹配条件查询数据类
* @param name 包名
* @return [PackageRulesData]
*/
internal fun createPackageRulesData(name: String) = PackageRulesData(name)
/**
* 获取 [Class.getSimpleName] 与 [Class.getName] 的独立名称
* @param instance 当前 [Class] 实例
* @return [String]
*/
internal fun classSingleName(instance: Class<*>) = instance.simpleName.takeIf { it.isNotBlank() }
?: instance.enclosingClass?.let { it.simpleName + instance.name.replace(it.name, newValue = "") } ?: ""
/**
* 类名匹配条件查询数据类
* @param name 包名
* @param isOptional 是否可选 - 默认否
*/
inner class NameRulesData internal constructor(var name: String, var isOptional: Boolean = false) {
/** [Class.getName] */
internal val TYPE_NAME = 0
/** [Class.getSimpleName] */
internal val TYPE_SIMPLE_NAME = 1
/** [Class.getSimpleName] or [Class.getName] */
internal val TYPE_SINGLE_NAME = 2
/**
* 匹配当前 [Class] 实例
* @param instance 当前 [Class] 实例
* @param type 判断类型
* @return [Boolean]
*/
internal fun equals(instance: Class<*>, type: Int) = when (type) {
TYPE_NAME -> instance.name == name
TYPE_SIMPLE_NAME -> instance.simpleName == name
TYPE_SINGLE_NAME -> classSingleName(instance) == name
else -> false
}
override fun toString() = "$name optional($isOptional)"
}
/**
* 包名范围名称过滤匹配条件查询数据类
* @param name 包名
* @param isAbsolute 是否绝对匹配 - 默认否
*/
inner class PackageRulesData internal constructor(var name: String, var isAbsolute: Boolean = false) {
override fun toString() = "$name absolute($isAbsolute)"
}
override val templates
get() = arrayOf(
fromPackages.takeIf { it.isNotEmpty() }?.let { "from:$it" } ?: "",
fullName?.let { "fullName:[$it]" } ?: "",
simpleName?.let { "simpleName:[$it]" } ?: "",
singleName?.let { "singleName:[$it]" } ?: "",
fullNameConditions?.let { "fullNameConditions:[existed]" } ?: "",
simpleNameConditions?.let { "simpleNameConditions:[existed]" } ?: "",
singleNameConditions?.let { "singleNameConditions:[existed]" } ?: "",
modifiers?.let { "modifiers:${ModifierRules.templates(uniqueValue)}" } ?: "",
isAnonymousClass?.let { "isAnonymousClass:[$it]" } ?: "",
isNoExtendsClass?.let { "isNoExtendsClass:[$it]" } ?: "",
isNoImplementsClass?.let { "isNoImplementsClass:[$it]" } ?: "",
extendsClass.takeIf { it.isNotEmpty() }?.let { "extendsClass:$it" } ?: "",
implementsClass.takeIf { it.isNotEmpty() }?.let { "implementsClass:$it" } ?: "",
enclosingClass.takeIf { it.isNotEmpty() }?.let { "enclosingClass:$it" } ?: "",
memberRules.takeIf { it.isNotEmpty() }?.let { "memberRules:[${it.size} existed]" } ?: "",
fieldRules.takeIf { it.isNotEmpty() }?.let { "fieldRules:[${it.size} existed]" } ?: "",
methodRules.takeIf { it.isNotEmpty() }?.let { "methodRules:[${it.size} existed]" } ?: "",
constroctorRules.takeIf { it.isNotEmpty() }?.let { "constroctorRules:[${it.size} existed]" } ?: ""
)
override val objectName get() = "Class"
override val isInitialize
get() = super.isInitialize || fromPackages.isNotEmpty() || fullName != null || simpleName != null || singleName != null ||
fullNameConditions != null || simpleNameConditions != null || singleNameConditions != null || isAnonymousClass != null ||
isNoExtendsClass != null || isNoImplementsClass != null || extendsClass.isNotEmpty() || enclosingClass.isNotEmpty() ||
memberRules.isNotEmpty() || fieldRules.isNotEmpty() || methodRules.isNotEmpty() || constroctorRules.isNotEmpty()
override fun hashCode(other: Any?) = super.hashCode(other) + toString().hashCode()
override fun toString() = "[$fromPackages][$fullName][$simpleName][$singleName][$fullNameConditions][$simpleNameConditions]" +
"[$singleNameConditions][$modifiers][$isAnonymousClass][$isNoExtendsClass][$isNoImplementsClass][$extendsClass][$implementsClass]" +
"[$enclosingClass][$memberRules][$fieldRules][$methodRules][$constroctorRules]"
}

View File

@@ -0,0 +1,145 @@
/*
* 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/9/12.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder.classes.rules
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.base.BaseRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.result.MemberRulesResult
import com.highcapable.yukihookapi.hook.core.finder.members.data.ConstructorRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.CountConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import java.lang.reflect.Constructor
/**
* [Constructor] 查询条件实现类
* @param rulesData 当前查询条件规则数据
*/
class ConstructorRules internal constructor(@PublishedApi internal val rulesData: ConstructorRulesData) : BaseRules() {
/**
* 设置 [Constructor] 参数个数
*
* 你可以不使用 [param] 指定参数类型而是仅使用此变量指定参数个数
*
* 若参数个数小于零则忽略并使用 [param]
* @return [Int]
*/
var paramCount
get() = rulesData.paramCount
set(value) {
rulesData.paramCount = value
}
/**
* 设置 [Constructor] 标识符筛选条件
*
* - 可不设置筛选条件
* @param conditions 条件方法体
*/
fun modifiers(conditions: ModifierConditions) {
rulesData.modifiers = conditions
}
/** 设置 [Constructor] 空参数、无参数 */
fun emptyParam() {
rulesData.paramCount = 0
}
/**
* 设置 [Constructor] 参数
*
* 如果同时使用了 [paramCount] 则 [paramType] 的数量必须与 [paramCount] 完全匹配
*
* 如果 [Constructor] 中存在一些无意义又很长的类型 - 你可以使用 [VagueType] 来替代它
*
* 例如下面这个参数结构 ↓
*
* ```java
* Foo(String var1, boolean var2, com.demo.Test var3, int var4)
* ```
*
* 此时就可以简单地写作 ↓
*
* ```kotlin
* param(StringType, BooleanType, VagueType, IntType)
* ```
*
* - ❗无参 [Constructor] 请使用 [emptyParam] 设置查询条件
*
* - ❗有参 [Constructor] 必须使用此方法设定参数或使用 [paramCount] 指定个数
* @param paramType 参数类型数组 - ❗只能是 [Class]、[String]、[VariousClass]
*/
fun param(vararg paramType: Any) {
if (paramType.isEmpty()) error("paramTypes is empty, please use emptyParam() instead")
rulesData.paramTypes =
arrayListOf<Class<*>>().apply { paramType.forEach { add(it.compat(tag = "Constructor") ?: UndefinedType) } }.toTypedArray()
}
/**
* 设置 [Constructor] 参数个数范围
*
* 你可以不使用 [param] 指定参数类型而是仅使用此方法指定参数个数范围
*
* 使用示例如下 ↓
*
* ```kotlin
* paramCount(1..5)
* ```
* @param numRange 个数范围
*/
fun paramCount(numRange: IntRange) {
rulesData.paramCountRange = numRange
}
/**
* 设置 [Constructor] 参数个数条件
*
* 你可以不使用 [param] 指定参数类型而是仅使用此方法指定参数个数条件
*
* 使用示例如下 ↓
*
* ```kotlin
* paramCount { it >= 5 || it.isZero() }
* ```
* @param conditions 条件方法体
*/
fun paramCount(conditions: CountConditions) {
rulesData.paramCountConditions = conditions
}
/**
* 返回结果实现类
* @return [MemberRulesResult]
*/
@PublishedApi
internal fun build() = MemberRulesResult(rulesData)
}

View File

@@ -0,0 +1,92 @@
/*
* 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/9/12.
*/
package com.highcapable.yukihookapi.hook.core.finder.classes.rules
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.base.BaseRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.result.MemberRulesResult
import com.highcapable.yukihookapi.hook.core.finder.members.data.FieldRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import java.lang.reflect.Field
/**
* [Field] 查询条件实现类
* @param rulesData 当前查询条件规则数据
*/
class FieldRules internal constructor(@PublishedApi internal val rulesData: FieldRulesData) : BaseRules() {
/**
* 设置 [Field] 名称
* @return [String]
*/
var name
get() = rulesData.name
set(value) {
rulesData.name = value
}
/**
* 设置 [Field] 类型
*
* - ❗只能是 [Class]、[String]、[VariousClass]
*
* - 可不填写类型
* @return [Any] or null
*/
var type
get() = rulesData.type
set(value) {
rulesData.type = value?.compat(tag = "Field")
}
/**
* 设置 [Field] 标识符筛选条件
*
* - 可不设置筛选条件
* @param conditions 条件方法体
*/
fun modifiers(conditions: ModifierConditions) {
rulesData.modifiers = conditions
}
/**
* 设置 [Field] 名称条件
* @param conditions 条件方法体
*/
fun name(conditions: NameConditions) {
rulesData.nameConditions = conditions
}
/**
* 返回结果实现类
* @return [MemberRulesResult]
*/
@PublishedApi
internal fun build() = MemberRulesResult(rulesData)
}

View File

@@ -0,0 +1,58 @@
/*
* 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/9/12.
*/
package com.highcapable.yukihookapi.hook.core.finder.classes.rules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.base.BaseRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.result.MemberRulesResult
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import java.lang.reflect.Member
/**
* [Member] 查询条件实现类
* @param rulesData 当前查询条件规则数据
*/
class MemberRules internal constructor(@PublishedApi internal val rulesData: MemberRulesData) : BaseRules() {
/**
* 设置 [Member] 标识符筛选条件
*
* - 可不设置筛选条件
* @param conditions 条件方法体
*/
fun modifiers(conditions: ModifierConditions) {
rulesData.modifiers = conditions
}
/**
* 返回结果实现类
* @return [MemberRulesResult]
*/
@PublishedApi
internal fun build() = MemberRulesResult(rulesData)
}

View File

@@ -0,0 +1,178 @@
/*
* 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/9/12.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder.classes.rules
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.base.BaseRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.result.MemberRulesResult
import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.CountConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.NameConditions
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import java.lang.reflect.Method
/**
* [Method] 查询条件实现类
* @param rulesData 当前查询条件规则数据
*/
class MethodRules internal constructor(@PublishedApi internal val rulesData: MethodRulesData) : BaseRules() {
/**
* 设置 [Method] 名称
* @return [String]
*/
var name
get() = rulesData.name
set(value) {
rulesData.name = value
}
/**
* 设置 [Method] 参数个数
*
* 你可以不使用 [param] 指定参数类型而是仅使用此变量指定参数个数
*
* 若参数个数小于零则忽略并使用 [param]
* @return [Int]
*/
var paramCount
get() = rulesData.paramCount
set(value) {
rulesData.paramCount = value
}
/**
* 设置 [Method] 返回值
*
* - ❗只能是 [Class]、[String]、[VariousClass]
*
* - 可不填写返回值
* @return [Any] or null
*/
var returnType
get() = rulesData.returnType
set(value) {
rulesData.returnType = value.compat(tag = "Method")
}
/**
* 设置 [Method] 标识符筛选条件
*
* - 可不设置筛选条件
* @param conditions 条件方法体
*/
fun modifiers(conditions: ModifierConditions) {
rulesData.modifiers = conditions
}
/** 设置 [Method] 空参数、无参数 */
fun emptyParam() {
rulesData.paramCount = 0
}
/**
* 设置 [Method] 参数
*
* 如果同时使用了 [paramCount] 则 [paramType] 的数量必须与 [paramCount] 完全匹配
*
* 如果 [Method] 中存在一些无意义又很长的类型 - 你可以使用 [VagueType] 来替代它
*
* 例如下面这个参数结构 ↓
*
* ```java
* void foo(String var1, boolean var2, com.demo.Test var3, int var4)
* ```
*
* 此时就可以简单地写作 ↓
*
* ```kotlin
* param(StringType, BooleanType, VagueType, IntType)
* ```
*
* - ❗无参 [Method] 请使用 [emptyParam] 设置查询条件
*
* - ❗有参 [Method] 必须使用此方法设定参数或使用 [paramCount] 指定个数
* @param paramType 参数类型数组 - ❗只能是 [Class]、[String]、[VariousClass]
*/
fun param(vararg paramType: Any) {
if (paramType.isEmpty()) error("paramTypes is empty, please use emptyParam() instead")
rulesData.paramTypes =
arrayListOf<Class<*>>().apply { paramType.forEach { add(it.compat(tag = "Method") ?: UndefinedType) } }.toTypedArray()
}
/**
* 设置 [Method] 名称条件
* @param conditions 条件方法体
*/
fun name(conditions: NameConditions) {
rulesData.nameConditions = conditions
}
/**
* 设置 [Method] 参数个数范围
*
* 你可以不使用 [param] 指定参数类型而是仅使用此方法指定参数个数范围
*
* 使用示例如下 ↓
*
* ```kotlin
* paramCount(1..5)
* ```
* @param numRange 个数范围
*/
fun paramCount(numRange: IntRange) {
rulesData.paramCountRange = numRange
}
/**
* 设置 [Method] 参数个数条件
*
* 你可以不使用 [param] 指定参数类型而是仅使用此方法指定参数个数条件
*
* 使用示例如下 ↓
*
* ```kotlin
* paramCount { it >= 5 || it.isZero() }
* ```
* @param conditions 条件方法体
*/
fun paramCount(conditions: CountConditions) {
rulesData.paramCountConditions = conditions
}
/**
* 返回结果实现类
* @return [MemberRulesResult]
*/
@PublishedApi
internal fun build() = MemberRulesResult(rulesData)
}

View File

@@ -0,0 +1,90 @@
/*
* 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/9/12.
*/
package com.highcapable.yukihookapi.hook.core.finder.classes.rules.base
import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.ConstructorRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.FieldRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.MemberRules
import com.highcapable.yukihookapi.hook.core.finder.classes.rules.MethodRules
import com.highcapable.yukihookapi.hook.core.finder.members.data.ConstructorRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.FieldRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData
import java.lang.reflect.Member
/**
* [Member] 查询条件实现父类
* @param instance 当前查找类实例
*/
open class BaseRules internal constructor(internal var instance: DexClassFinder? = null) {
@PublishedApi
internal companion object {
/**
* 创建查询条件规则数据
* @param instance 当前查找类实例
* @return [MemberRulesData]
*/
@PublishedApi
internal fun createMemberRules(instance: DexClassFinder) =
MemberRules(MemberRulesData().apply { instance.rulesData.memberRules.add(this) }).apply { this.instance = instance }
/**
* 创建查询条件规则数据
* @return [FieldRulesData]
*/
@PublishedApi
internal fun createFieldRules(instance: DexClassFinder) =
FieldRules(FieldRulesData().apply { instance.rulesData.fieldRules.add(this) }).apply { this.instance = instance }
/**
* 创建查询条件规则数据
* @return [MethodRulesData]
*/
@PublishedApi
internal fun createMethodRules(instance: DexClassFinder) =
MethodRules(MethodRulesData().apply { instance.rulesData.methodRules.add(this) }).apply { this.instance = instance }
/**
* 创建查询条件规则数据
* @return [ConstructorRulesData]
*/
@PublishedApi
internal fun createConstructorRules(instance: DexClassFinder) =
ConstructorRules(ConstructorRulesData().apply { instance.rulesData.constroctorRules.add(this) }).apply { this.instance = instance }
}
/**
* 将目标类型转换为可识别的兼容类型
* @param tag 当前查找类的标识
* @return [Class] or null
*/
internal fun Any?.compat(tag: String) = instance?.compatType(any = this, tag)
}

View File

@@ -0,0 +1,89 @@
/*
* 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/9/12.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder.classes.rules.result
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.type.factory.CountConditions
import java.lang.reflect.Member
/**
* 当前 [Member] 查询条件结果实现类
* @param rulesData 当前查询条件规则数据
*/
class MemberRulesResult internal constructor(private val rulesData: MemberRulesData) {
/**
* 设置当前 [Member] 在查询条件中个数为 0
* @return [MemberRulesResult] 可继续向下监听
*/
fun none() = count(num = 0)
/**
* 设置当前 [Member] 在查询条件中需要全部匹配的个数
* @param num 个数
* @return [MemberRulesResult] 可继续向下监听
*/
fun count(num: Int): MemberRulesResult {
rulesData.matchCount = num
return this
}
/**
* 设置当前 [Member] 在查询条件中需要全部匹配的个数范围
*
* 使用示例如下 ↓
*
* ```kotlin
* count(1..5)
* ```
* @param numRange 个数范围
* @return [MemberRulesResult] 可继续向下监听
*/
fun count(numRange: IntRange): MemberRulesResult {
rulesData.matchCountRange = numRange
return this
}
/**
* 设置当前 [Member] 在查询条件中需要全部匹配的个数条件
*
* 使用示例如下 ↓
*
* ```kotlin
* count { it >= 5 || it.isZero() }
* ```
* @param conditions 条件方法体
* @return [MemberRulesResult] 可继续向下监听
*/
fun count(conditions: CountConditions): MemberRulesResult {
rulesData.matchCountConditions = conditions
return this
}
}

View File

@@ -30,28 +30,34 @@
package com.highcapable.yukihookapi.hook.core.finder.tools
import com.highcapable.yukihookapi.hook.core.finder.base.data.BaseRulesData
import com.highcapable.yukihookapi.hook.core.finder.base.rules.CountRules
import com.highcapable.yukihookapi.hook.core.finder.classes.data.ClassRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.ConstructorRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.FieldRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MemberRulesData
import com.highcapable.yukihookapi.hook.core.finder.members.data.MethodRulesData
import com.highcapable.yukihookapi.hook.factory.hasExtends
import com.highcapable.yukihookapi.hook.factory.*
import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.store.ReflectsCacheStore
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
import com.highcapable.yukihookapi.hook.type.defined.VagueType
import com.highcapable.yukihookapi.hook.type.java.DalvikBaseDexClassLoader
import com.highcapable.yukihookapi.hook.type.java.NoClassDefFoundErrorClass
import com.highcapable.yukihookapi.hook.type.java.NoSuchFieldErrorClass
import com.highcapable.yukihookapi.hook.type.java.NoSuchMethodErrorClass
import com.highcapable.yukihookapi.hook.utils.conditions
import com.highcapable.yukihookapi.hook.utils.let
import com.highcapable.yukihookapi.hook.utils.takeIf
import com.highcapable.yukihookapi.hook.utils.value
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import dalvik.system.BaseDexClassLoader
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.util.*
import kotlin.math.abs
/**
@@ -62,6 +68,23 @@ internal object ReflectionTool {
/** 当前工具类的标签 */
private const val TAG = "YukiHookAPI#ReflectionTool"
/**
* 写出当前 [ClassLoader] 下所有 [Class] 名称数组
* @param loader 当前使用的 [ClassLoader]
* @return [List]<[String]>
* @throws IllegalStateException 如果 [loader] 不是 [BaseDexClassLoader]
*/
private fun findDexClassList(loader: ClassLoader?) = ReflectsCacheStore.findDexClassList(loader.hashCode())
?: DalvikBaseDexClassLoader.field { name = "pathList" }.ignored().get(loader.value().let {
while (it.value !is BaseDexClassLoader) {
if (it.value?.parent != null) it.value = it.value?.parent
else error("ClassLoader [$loader] is not a DexClassLoader")
}; it.value ?: error("ClassLoader [$loader] load failed")
}).current(ignored = true)?.field { name = "dexElements" }?.array<Any>()?.flatMap { element ->
element.current(ignored = true).field { name = "dexFile" }.current(ignored = true)
?.method { name = "entries" }?.invoke<Enumeration<String>>()?.toList().orEmpty()
}.orEmpty().also { if (it.isNotEmpty()) ReflectsCacheStore.putDexClassList(loader.hashCode(), it) }
/**
* 使用字符串类名查询 [Class] 是否存在
* @param name [Class] 完整名称
@@ -89,6 +112,140 @@ internal object ReflectionTool {
}.getOrNull() ?: throw createException(loader ?: AppParasitics.baseClassLoader, name = "Class", "name:[$name]")
}
/**
* 查找任意 [Class] 或一组 [Class]
* @param loaderSet 类所在 [ClassLoader]
* @param rulesData 规则查询数据
* @return [HashSet]<[Class]>
* @throws IllegalStateException 如果 [loaderSet] 为 null 或未设置任何条件
* @throws NoClassDefFoundError 如果找不到 [Class]
*/
internal fun findClasses(loaderSet: ClassLoader?, rulesData: ClassRulesData) = rulesData.createResult {
ReflectsCacheStore.findClasses(hashCode(loaderSet)) ?: hashSetOf<Class<*>>().also { classes ->
/**
* 开始查询作业
* @param instance 当前 [Class] 实例
*/
fun startProcess(instance: Class<*>) {
conditions {
fromPackages.takeIf { it.isNotEmpty() }?.also { and(true) }
fullName?.also { it.equals(instance, it.TYPE_NAME).also { e -> if (it.isOptional) opt(e) else and(e) } }
simpleName?.also { it.equals(instance, it.TYPE_SIMPLE_NAME).also { e -> if (it.isOptional) opt(e) else and(e) } }
singleName?.also { it.equals(instance, it.TYPE_SINGLE_NAME).also { e -> if (it.isOptional) opt(e) else and(e) } }
fullNameConditions?.also { instance.name.also { n -> and(it(n.cast(), n)) } }
simpleNameConditions?.also { instance.simpleName.also { n -> and(it(n.cast(), n)) } }
singleNameConditions?.also { classSingleName(instance).also { n -> and(it(n.cast(), n)) } }
modifiers?.also { and(it(instance.cast())) }
extendsClass.takeIf { it.isNotEmpty() }?.also { and(instance.hasExtends && it.contains(instance.superclass.name)) }
implementsClass.takeIf { it.isNotEmpty() }
?.also { and(instance.interfaces.isNotEmpty() && instance.interfaces.any { e -> it.contains(e.name) }) }
enclosingClass.takeIf { it.isNotEmpty() }
?.also { and(instance.enclosingClass != null && it.contains(instance.enclosingClass.name)) }
isAnonymousClass?.also { and(instance.isAnonymousClass && it) }
isNoExtendsClass?.also { and(instance.hasExtends.not() && it) }
isNoImplementsClass?.also { and(instance.interfaces.isEmpty() && it) }
/**
* 匹配 [MemberRulesData]
* @param size [Member] 个数
* @param result 回调是否匹配
*/
fun MemberRulesData.matchCount(size: Int, result: (Boolean) -> Unit) {
takeIf { it.isInitializeOfMatch }?.also { rule ->
rule.conditions {
value.matchCount.takeIf { it >= 0 }?.also { and(it == size) }
value.matchCountRange.takeIf { it.isEmpty().not() }?.also { and(size in it) }
value.matchCountConditions?.also { and(it(CountRules.with(size), size)) }
}.finally { result(true) }.without { result(false) }
} ?: result(true)
}
/**
* 检查类型中的 [Class] 是否存在 - 即不存在 [UndefinedType]
* @param type 类型
* @return [Boolean]
*/
fun MemberRulesData.exists(vararg type: Any?): Boolean {
if (type.isEmpty()) return true
for (i in type.indices) if (type[i] == UndefinedType) {
yLoggerW(msg = "$objectName type[$i] mistake, it will be ignored in current conditions")
return false
}
return true
}
memberRules.takeIf { it.isNotEmpty() }?.forEach { rule ->
instance.existMembers?.apply {
var numberOfFound = 0
if (rule.isInitializeOfSuper) forEach { member ->
rule.conditions {
value.modifiers?.also { and(it(member.cast())) }
}.finally { numberOfFound++ }
}.run { rule.matchCount(numberOfFound) { and(it && numberOfFound > 0) } }
else rule.matchCount(size) { and(it) }
}
}
fieldRules.takeIf { it.isNotEmpty() }?.forEach { rule ->
instance.existFields?.apply {
var numberOfFound = 0
if (rule.isInitialize) forEach { field ->
rule.conditions {
value.type?.also { value.exists(it) and (it == field.type) }
value.name.takeIf { it.isNotBlank() }?.also { and(it == field.name) }
value.modifiers?.also { and(it(field.cast())) }
value.nameConditions?.also { field.name.also { n -> and(it(n.cast(), n)) } }
}.finally { numberOfFound++ }
}.run { rule.matchCount(numberOfFound) { and(it && numberOfFound > 0) } }
else rule.matchCount(size) { and(it) }
}
}
methodRules.takeIf { it.isNotEmpty() }?.forEach { rule ->
instance.existMethods?.apply {
var numberOfFound = 0
if (rule.isInitialize) forEach { method ->
rule.conditions {
value.name.takeIf { it.isNotBlank() }?.also { and(it == method.name) }
value.returnType?.also { value.exists(it) and (it == method.returnType) }
value.paramCount.takeIf { it >= 0 }?.also { and(method.parameterTypes.size == it) }
value.paramCountRange.takeIf { it.isEmpty().not() }?.also { and(method.parameterTypes.size in it) }
value.paramCountConditions?.also { method.parameterTypes.size.also { s -> and(it(s.cast(), s)) } }
value.paramTypes?.also { value.exists(*it) and (paramTypesEq(it, method.parameterTypes)) }
value.modifiers?.also { and(it(method.cast())) }
value.nameConditions?.also { method.name.also { n -> and(it(n.cast(), n)) } }
}.finally { numberOfFound++ }
}.run { rule.matchCount(numberOfFound) { and(it && numberOfFound > 0) } }
else rule.matchCount(size) { and(it) }
}
}
constroctorRules.takeIf { it.isNotEmpty() }?.forEach { rule ->
instance.existConstructors?.apply {
var numberOfFound = 0
if (rule.isInitialize) forEach { constructor ->
rule.conditions {
value.paramCount.takeIf { it >= 0 }?.also { and(constructor.parameterTypes.size == it) }
value.paramCountRange.takeIf { it.isEmpty().not() }?.also { and(constructor.parameterTypes.size in it) }
value.paramCountConditions?.also { constructor.parameterTypes.size.also { s -> and(it(s.cast(), s)) } }
value.paramTypes?.also { value.exists(*it) and (paramTypesEq(it, constructor.parameterTypes)) }
value.modifiers?.also { and(it(constructor.cast())) }
}.finally { numberOfFound++ }
}.run { rule.matchCount(numberOfFound) { and(it && numberOfFound > 0) } }
else rule.matchCount(size) { and(it) }
}
}
}.finally { classes.add(instance) }
}
findDexClassList(loaderSet).takeIf { it.isNotEmpty() }?.forEach { className ->
/** 分离包名 → com.demo.Test → com.demo (获取最后一个 "." + 简单类名的长度) → 由于末位存在 "." 最后要去掉 1 个长度 */
(if (className.contains(other = "."))
className.substring(0, className.length - className.split(".").let { it[it.lastIndex] }.length - 1)
else className).also { packageName ->
if ((fromPackages.isEmpty() || fromPackages.any {
if (it.isAbsolute) packageName == it.name else packageName.startsWith(it.name)
}) && className.hasClass(loaderSet)
) startProcess(className.toClass(loaderSet))
}
}
}.takeIf { it.isNotEmpty() }?.also { ReflectsCacheStore.putClasses(hashCode(loaderSet), it) } ?: throwNotFoundError(loaderSet)
}
/**
* 查找任意 [Field] 或一组 [Field]
* @param classSet [Field] 所在类
@@ -333,6 +490,7 @@ internal object ReflectionTool {
is FieldRulesData -> isInitialize.not()
is MethodRulesData -> isInitialize.not()
is ConstructorRulesData -> isInitialize.not()
is ClassRulesData -> isInitialize.not()
else -> true
}.takeIf { it }?.also { error("You must set a condition when finding a $objectName") }
return result(this)
@@ -374,6 +532,7 @@ internal object ReflectionTool {
is FieldRulesData -> throw createException(instanceSet, objectName, *templates)
is MethodRulesData -> throw createException(instanceSet, objectName, *templates)
is ConstructorRulesData -> throw createException(instanceSet, objectName, *templates)
is ClassRulesData -> throw createException(instanceSet ?: AppParasitics.baseClassLoader, objectName, *templates)
else -> error("Type [$this] not allowed")
}

View File

@@ -30,10 +30,14 @@ package com.highcapable.yukihookapi.hook.core.finder.type.factory
import com.highcapable.yukihookapi.hook.core.finder.base.rules.CountRules
import com.highcapable.yukihookapi.hook.core.finder.base.rules.ModifierRules
import com.highcapable.yukihookapi.hook.core.finder.base.rules.NameRules
import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder
import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.members.FieldFinder
import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder
/** 定义 [DexClassFinder] 方法体类型 */
internal typealias ClassConditions = DexClassFinder.() -> Unit
/** 定义 [FieldFinder] 方法体类型 */
internal typealias FieldConditions = FieldFinder.() -> Unit

View File

@@ -33,14 +33,12 @@ import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.bean.CurrentClass
import com.highcapable.yukihookapi.hook.bean.GenericClass
import com.highcapable.yukihookapi.hook.core.finder.base.rules.ModifierRules
import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder
import com.highcapable.yukihookapi.hook.core.finder.members.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.members.FieldFinder
import com.highcapable.yukihookapi.hook.core.finder.members.MethodFinder
import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ConstructorConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.FieldConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.MethodConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ModifierConditions
import com.highcapable.yukihookapi.hook.core.finder.type.factory.*
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
import java.lang.reflect.*
@@ -59,6 +57,22 @@ enum class MembersType {
CONSTRUCTOR
}
/**
* 通过当前 [ClassLoader] 按指定条件查找并得到 Dex 中的 [Class]
*
* - ❗此方法在 [Class] 数量过多及查找条件复杂时会非常耗时
*
* - ❗建议启用 [async] 或设置 [name] 参数 - [name] 参数将在 Hook APP (宿主) 不同版本中自动进行本地缓存以提升效率
*
* - ❗此功能尚在试验阶段 - 性能与稳定性可能仍然存在问题 - 使用过程遇到问题请向我们报告并帮助我们改进
* @param name 标识当前 [Class] 缓存的名称 - 不设置将不启用缓存 - 启用缓存自动启用 [async]
* @param async 是否启用异步 - 默认否
* @param initiate 方法体
* @return [DexClassFinder.Result]
*/
inline fun ClassLoader.searchClass(name: String = "", async: Boolean = false, initiate: ClassConditions) =
DexClassFinder(name, async = async || name.isNotBlank(), loaderSet = this).apply(initiate).build()
/**
* 监听当前 [ClassLoader] 的 [ClassLoader.loadClass] 方法装载
*

View File

@@ -42,7 +42,9 @@ import com.highcapable.yukihookapi.hook.bean.HookResources
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator
import com.highcapable.yukihookapi.hook.core.YukiResourcesHookCreator
import com.highcapable.yukihookapi.hook.core.finder.classes.DexClassFinder
import com.highcapable.yukihookapi.hook.core.finder.tools.ReflectionTool
import com.highcapable.yukihookapi.hook.core.finder.type.factory.ClassConditions
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
@@ -292,6 +294,22 @@ open class PackageParam internal constructor(@PublishedApi internal var wrapper:
*/
fun loadHooker(hooker: YukiBaseHooker) = hooker.assignInstance(packageParam = this)
/**
* 通过 [appClassLoader] 按指定条件查找并得到当前 Hook APP Dex 中的 [Class]
*
* - ❗此方法在 [Class] 数量过多及查找条件复杂时会非常耗时
*
* - ❗建议启用 [async] 或设置 [name] 参数 - [name] 参数将在 Hook APP (宿主) 不同版本中自动进行本地缓存以提升效率
*
* - ❗此功能尚在试验阶段 - 性能与稳定性可能仍然存在问题 - 使用过程遇到问题请向我们报告并帮助我们改进
* @param name 标识当前 [Class] 缓存的名称 - 不设置将不启用缓存 - 启用缓存自动启用 [async]
* @param async 是否启用异步 - 默认否
* @param initiate 方法体
* @return [DexClassFinder.Result]
*/
inline fun searchClass(name: String = "", async: Boolean = false, initiate: ClassConditions) =
DexClassFinder(name, async = async || name.isNotBlank(), appClassLoader).apply(initiate).build()
/**
* 通过字符串类名转换为当前 Hook APP 的实体类
*

View File

@@ -38,13 +38,21 @@ import java.lang.reflect.Method
*
* 为防止 [Class]、[Member] 复用过高造成的系统 GC 问题
*
* 查后的 [Class]、[Member] 在 [YukiHookAPI.Configs.isEnableMemberCache] 启用后自动进入缓存
* 查后的 [Class] 自动进入缓存 - 不受任何控制
*
* 查找后的 [Member] 在 [YukiHookAPI.Configs.isEnableMemberCache] 启用后自动进入缓存
*/
internal object ReflectsCacheStore {
/** 缓存的 [Class] */
/** 缓存的 [Class] 列表 */
private val dexClassListData = HashMap<Int, List<String>>()
/** 缓存的 [Class] 对象 */
private val classData = HashMap<Int, Class<*>?>()
/** 缓存的 [Class] 数组 */
private val classesData = HashMap<Int, HashSet<Class<*>>>()
/** 缓存的 [Method] 数组 */
private val methodsData = HashMap<Int, HashSet<Method>>()
@@ -54,6 +62,13 @@ internal object ReflectsCacheStore {
/** 缓存的 [Field] 数组 */
private val fieldsData = HashMap<Int, HashSet<Field>>()
/**
* 查找缓存中的 [Class] 列表
* @param hashCode 标识符
* @return [List]<[Class]>
*/
internal fun findDexClassList(hashCode: Int) = dexClassListData[hashCode]
/**
* 查找缓存中的 [Class]
* @param hashCode 标识符
@@ -61,6 +76,13 @@ internal object ReflectsCacheStore {
*/
internal fun findClass(hashCode: Int) = classData[hashCode]
/**
* 查找缓存中的 [Class] 数组
* @param hashCode 标识符
* @return [HashSet]<[Class]> or null
*/
internal fun findClasses(hashCode: Int) = classesData[hashCode]
/**
* 查找缓存中的 [Method] 数组
* @param hashCode 标识符
@@ -82,16 +104,33 @@ internal object ReflectsCacheStore {
*/
internal fun findFields(hashCode: Int) = fieldsData[hashCode]
/**
* 写入 [Class] 列表到缓存
* @param hashCode 标识符
* @param instance 实例
*/
internal fun putDexClassList(hashCode: Int, instance: List<String>) {
dexClassListData[hashCode] = instance
}
/**
* 写入 [Class] 到缓存
* @param hashCode 标识符
* @param instance 实例
*/
internal fun putClass(hashCode: Int, instance: Class<*>?) {
if (YukiHookAPI.Configs.isEnableMemberCache.not()) return
classData[hashCode] = instance
}
/**
* 写入 [Class] 数组到缓存
* @param hashCode 标识符
* @param instance 实例
*/
internal fun putClasses(hashCode: Int, instance: HashSet<Class<*>>) {
classesData[hashCode] = instance
}
/**
* 写入 [Method] 数组到缓存
* @param hashCode 标识符