Fix LSPosed when opened "只有模块classloader可以使用Xposed API" not founded the API's bug And add more feature function

This commit is contained in:
2022-03-06 00:13:43 +08:00
parent ca01a47efa
commit c1797ebade
14 changed files with 107 additions and 35 deletions

View File

@@ -37,7 +37,7 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
import java.io.File import java.io.File
/** /**
* 这是 YukiHookAPI 的自动生成处理类 - 核心基于 KSP * 这是 [YukiHookAPI] 的自动生成处理类 - 核心基于 KSP
* *
* 可以帮你快速生成 Xposed 入口类和包名 * 可以帮你快速生成 Xposed 入口类和包名
* *

View File

@@ -36,22 +36,21 @@ import com.highcapable.yukihookapi.YukiHookAPI.encase
import com.highcapable.yukihookapi.annotation.DoNotUseField import com.highcapable.yukihookapi.annotation.DoNotUseField
import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.processName import com.highcapable.yukihookapi.hook.factory.processName
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.*
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage import de.robv.android.xposed.callbacks.XC_LoadPackage
/** /**
* YukiHookAPI 的装载调用类 * [YukiHookAPI] 的装载调用类
* *
* 可以实现作为模块装载和自定义 Hook 装载两种方式 * 可以实现作为模块装载和自定义 Hook 装载两种方式
* *
* 模块装载方式已经自动对接 Xposed API - 可直接调用 [encase] 完成操作 * 模块装载方式已经自动对接 Xposed API - 可直接调用 [encase] 完成操作
* *
* 你可以调用 [configs] 对 YukiHookAPI 进行配置 * 你可以调用 [configs] 对 [YukiHookAPI] 进行配置
*/ */
object YukiHookAPI { object YukiHookAPI {
@@ -97,10 +96,21 @@ object YukiHookAPI {
* 请过滤 [debugTag] 即可找到每条日志 * 请过滤 [debugTag] 即可找到每条日志
*/ */
var isDebug = true var isDebug = true
/**
* 是否启用调试日志的输出功能
*
* - ❗关闭后将会停用 [YukiHookAPI] 对全部日志的输出
*
* 但是不影响当你手动调用下面这些方法输出日志
*
* [loggerD]、[loggerI]、[loggerW]、[loggerE]
*/
var isAllowPrintingLogs = true
} }
/** /**
* 配置 YukiHookAPI 相关参数 * 配置 [YukiHookAPI] 相关参数
* *
* 详情请参考 [configs 方法](https://github.com/fankes/YukiHookAPI/wiki/API-%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE#configs-%E6%96%B9%E6%B3%95) * 详情请参考 [configs 方法](https://github.com/fankes/YukiHookAPI/wiki/API-%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE#configs-%E6%96%B9%E6%B3%95)
* @param initiate 方法体 * @param initiate 方法体
@@ -207,7 +217,7 @@ object YukiHookAPI {
} }
/** 输出找不到 [XposedBridge] 的错误日志 */ /** 输出找不到 [XposedBridge] 的错误日志 */
private fun printNoXposedBridge() = loggerE(msg = "Could not found XposedBridge in current space! Aborted") private fun printNoXposedBridge() = yLoggerE(msg = "Could not found XposedBridge in current space! Aborted")
/** /**
* 通过 baseContext 创建 Hook 入口类 * 通过 baseContext 创建 Hook 入口类
@@ -220,5 +230,9 @@ object YukiHookAPI {
* 是否存在 [XposedBridge] * 是否存在 [XposedBridge]
* @return [Boolean] * @return [Boolean]
*/ */
internal val hasXposedBridge get() = ("de.robv.android.xposed.XposedBridge").hasClass internal val hasXposedBridge
get() = runCatching {
if (Configs.isDebug) yLoggerI(msg = "YukiHookAPI is running on Xposed API ${XposedBridge.getXposedVersion()}")
true
}.getOrNull() ?: false
} }

View File

@@ -29,10 +29,11 @@
package com.highcapable.yukihookapi.annotation.xposed package com.highcapable.yukihookapi.annotation.xposed
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
/** /**
* 标识 YukiHookAPI 注入 Xposed 入口的类注释 * 标识 [YukiHookAPI] 注入 Xposed 入口的类注释
* *
* - 你的项目 source 目录默认为 "src/main/" 可在 [sourcePath] 中进行自定义 - 自动处理程序将只检查 ..app/[sourcePath]/java.. 中间部分 * - 你的项目 source 目录默认为 "src/main/" 可在 [sourcePath] 中进行自定义 - 自动处理程序将只检查 ..app/[sourcePath]/java.. 中间部分
* *

View File

@@ -36,8 +36,8 @@ import com.highcapable.yukihookapi.hook.bean.HookClass
import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
import com.highcapable.yukihookapi.hook.core.finder.FieldFinder import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.log.yLoggerI
import com.highcapable.yukihookapi.hook.param.HookParam import com.highcapable.yukihookapi.hook.param.HookParam
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
@@ -48,7 +48,7 @@ import java.lang.reflect.Field
import java.lang.reflect.Member import java.lang.reflect.Member
/** /**
* YukiHookAPI 核心 Hook 实现类 * [YukiHookAPI] 核心 Hook 实现类
* *
* 这是一个 API 对接类 - 实现原生对接 [XposedBridge] * 这是一个 API 对接类 - 实现原生对接 [XposedBridge]
* @param packageParam 需要传入 [PackageParam] 实现方法调用 * @param packageParam 需要传入 [PackageParam] 实现方法调用
@@ -103,7 +103,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
else Thread { else Thread {
SystemClock.sleep(10) SystemClock.sleep(10)
if (onHookClassNotFoundFailureCallback == null) if (onHookClassNotFoundFailureCallback == null)
loggerE(msg = "HookClass [${hookClass.name}] not found", e = hookClass.throwable) yLoggerE(msg = "HookClass [${hookClass.name}] not found", e = hookClass.throwable)
else onHookClassNotFoundFailureCallback?.invoke(hookClass.throwable ?: Throwable("[${hookClass.name}] not found")) else onHookClassNotFoundFailureCallback?.invoke(hookClass.throwable ?: Throwable("[${hookClass.name}] not found"))
}.start() }.start()
return Result() return Result()
@@ -427,7 +427,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
onHookingFailureCallback?.invoke(it) onHookingFailureCallback?.invoke(it)
onAllFailureCallback?.invoke(it) onAllFailureCallback?.invoke(it)
if (isNotIgnoredHookingFailure) if (isNotIgnoredHookingFailure)
loggerE( yLoggerE(
msg = if (isHookMemberSetup) msg = if (isHookMemberSetup)
"Hooked Member with a finding error by $hookClass [$tag]" "Hooked Member with a finding error by $hookClass [$tag]"
else "Hooked Member cannot be non-null by $hookClass [$tag]", else "Hooked Member cannot be non-null by $hookClass [$tag]",
@@ -448,7 +448,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
} }
}.onFailure { }.onFailure {
onAllFailureCallback?.invoke(it) onAllFailureCallback?.invoke(it)
if (isNotIgnoredHookingFailure) loggerE(msg = "Hooked All Members with an error in Class [$hookClass] [$tag]") if (isNotIgnoredHookingFailure) yLoggerE(msg = "Hooked All Members with an error in Class [$hookClass] [$tag]")
} }
} }
@@ -457,7 +457,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
* @param msg 调试日志内容 * @param msg 调试日志内容
*/ */
private fun onHookLogMsg(msg: String) { private fun onHookLogMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug) loggerI(msg = msg) if (YukiHookAPI.Configs.isDebug) yLoggerI(msg = msg)
} }
/** /**
@@ -465,7 +465,7 @@ class YukiHookCreater(private val packageParam: PackageParam, private val hookCl
* @param throwable 异常信息 * @param throwable 异常信息
*/ */
private fun onHookFailureMsg(throwable: Throwable) = private fun onHookFailureMsg(throwable: Throwable) =
loggerE(msg = "Try to hook ${hookClass.instance ?: hookClass.name}[$member] got an Exception [$tag]", e = throwable) yLoggerE(msg = "Try to hook ${hookClass.instance ?: hookClass.name}[$member] got an Exception [$tag]", e = throwable)
/** /**
* 判断是否没有设置 Hook 过程中的任何异常拦截 * 判断是否没有设置 Hook 过程中的任何异常拦截

View File

@@ -32,7 +32,7 @@ package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Constructor import java.lang.reflect.Constructor
@@ -177,7 +177,7 @@ class ConstructorFinder(
) )
remedyPlans.clear() remedyPlans.clear()
} }
} else loggerW(msg = "RemedyPlan is empty,forgot it? [${hookTag}]") } else yLoggerW(msg = "RemedyPlan is empty,forgot it? [${hookTag}]")
} }
/** /**

View File

@@ -33,7 +33,7 @@ import android.os.SystemClock
import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Field import java.lang.reflect.Field
@@ -75,7 +75,7 @@ class FieldFinder(
@DoNotUseMethod @DoNotUseMethod
override fun build(isBind: Boolean) = when { override fun build(isBind: Boolean) = when {
name.isBlank() -> { name.isBlank() -> {
loggerE(msg = "Field name cannot be empty in Class [$classSet] [${hookTag}]") yLoggerE(msg = "Field name cannot be empty in Class [$classSet] [${hookTag}]")
Result(isNoSuch = true) Result(isNoSuch = true)
} }
else -> try { else -> try {
@@ -91,7 +91,7 @@ class FieldFinder(
} catch (e: Throwable) { } catch (e: Throwable) {
Thread { Thread {
SystemClock.sleep(10) SystemClock.sleep(10)
if (isNotIgnoredHookingFailure) loggerE(msg = "NoSuchField happend in [$classSet] [${hookTag}]", e = e) if (isNotIgnoredHookingFailure) yLoggerE(msg = "NoSuchField happend in [$classSet] [${hookTag}]", e = e)
}.start() }.start()
Result(isNoSuch = true, e) Result(isNoSuch = true, e)
} }

View File

@@ -32,8 +32,8 @@ package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.log.yLoggerW
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import com.highcapable.yukihookapi.hook.utils.runBlocking import com.highcapable.yukihookapi.hook.utils.runBlocking
import java.lang.reflect.Method import java.lang.reflect.Method
@@ -117,7 +117,7 @@ class MethodFinder(
@DoNotUseMethod @DoNotUseMethod
override fun build(isBind: Boolean) = when { override fun build(isBind: Boolean) = when {
name.isBlank() -> { name.isBlank() -> {
loggerE(msg = "Method name cannot be empty in Class [$classSet] [${hookTag}]") yLoggerE(msg = "Method name cannot be empty in Class [$classSet] [${hookTag}]")
Result(isNoSuch = true) Result(isNoSuch = true)
} }
else -> try { else -> try {
@@ -201,7 +201,7 @@ class MethodFinder(
) )
remedyPlans.clear() remedyPlans.clear()
} }
} else loggerW(msg = "RemedyPlan is empty,forgot it? [${hookTag}]") } else yLoggerW(msg = "RemedyPlan is empty,forgot it? [${hookTag}]")
} }
/** /**

View File

@@ -31,8 +31,8 @@ import android.os.SystemClock
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.log.loggerE import com.highcapable.yukihookapi.hook.log.yLoggerE
import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.log.yLoggerI
import java.lang.reflect.Member import java.lang.reflect.Member
/** /**
@@ -75,7 +75,7 @@ abstract class BaseFinder(
* @param isAlwaysPrint 忽略条件每次都打印错误 * @param isAlwaysPrint 忽略条件每次都打印错误
*/ */
internal fun onFailureMsg(msg: String = "", throwable: Throwable? = null, isAlwaysPrint: Boolean = false) { internal fun onFailureMsg(msg: String = "", throwable: Throwable? = null, isAlwaysPrint: Boolean = false) {
fun print() = loggerE(msg = "NoSuch$tag happend in [$classSet] $msg [${hookTag}]", e = throwable) fun print() = yLoggerE(msg = "NoSuch$tag happend in [$classSet] $msg [${hookTag}]", e = throwable)
if (isAlwaysPrint) print() if (isAlwaysPrint) print()
else Thread { else Thread {
SystemClock.sleep(10) SystemClock.sleep(10)
@@ -88,7 +88,7 @@ abstract class BaseFinder(
* @param msg 调试日志内容 * @param msg 调试日志内容
*/ */
internal fun onHookLogMsg(msg: String) { internal fun onHookLogMsg(msg: String) {
if (YukiHookAPI.Configs.isDebug) loggerI(msg = msg) if (YukiHookAPI.Configs.isDebug) yLoggerI(msg = msg)
} }
/** /**

View File

@@ -34,7 +34,7 @@ import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
/** /**
* YukiHookAPI 的子类 Hooker 实现 * [YukiHookAPI] 的子类 Hooker 实现
* *
* 也许你的 Module 中存在多个 Hooker - 继承此类可以方便帮你管理每个 Hooker * 也许你的 Module 中存在多个 Hooker - 继承此类可以方便帮你管理每个 Hooker
* *

View File

@@ -78,6 +78,22 @@ fun String.hasClass(loader: ClassLoader?) = try {
false false
} }
/**
* 查找变量是否存在
* @param name 名称
* @param type 类型 - 不填默认模糊
* @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
}
/** /**
* 查找方法是否存在 * 查找方法是否存在
* @param name 名称 * @param name 名称

View File

@@ -33,6 +33,47 @@ import android.util.Log
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
/**
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - D
*
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
* @param msg 日志打印的内容
*/
internal fun yLoggerD(msg: String) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) loggerD(msg = msg)
}
/**
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - I
*
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
* @param msg 日志打印的内容
*/
internal fun yLoggerI(msg: String) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) loggerI(msg = msg)
}
/**
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - W
*
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
* @param msg 日志打印的内容
*/
internal fun yLoggerW(msg: String) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) loggerW(msg = msg)
}
/**
* [YukiHookAPI] 向控制台和 [XposedBridge] 打印日志 - E
*
* - ❗此方法为私有功能性 API - 你不应该手动调用此方法
* @param msg 日志打印的内容
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
*/
internal fun yLoggerE(msg: String, e: Throwable? = null) {
if (YukiHookAPI.Configs.isAllowPrintingLogs) loggerE(msg = msg, e = e)
}
/** /**
* 向控制台和 [XposedBridge] 打印日志 - D * 向控制台和 [XposedBridge] 打印日志 - D
* *

View File

@@ -28,7 +28,7 @@
package com.highcapable.yukihookapi.hook.xposed package com.highcapable.yukihookapi.hook.xposed
import androidx.annotation.Keep import androidx.annotation.Keep
import com.highcapable.yukihookapi.hook.log.loggerI import com.highcapable.yukihookapi.hook.log.yLoggerI
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus.isActive import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus.isActive
/** /**
@@ -47,7 +47,7 @@ object YukiHookModuleStatus {
*/ */
@Keep @Keep
fun isActive(): Boolean { fun isActive(): Boolean {
loggerI(msg = "This Module is not actived") yLoggerI(msg = "This Module is not actived")
return false return false
} }
} }

View File

@@ -49,7 +49,7 @@ import java.io.File
* *
* - 使用 LSPosed 环境请在 AndroidManifests.xml 中将 "xposedminversion" 最低设置为 93 * - 使用 LSPosed 环境请在 AndroidManifests.xml 中将 "xposedminversion" 最低设置为 93
* *
* - 未使用 LSPosed 环境请将你的模块 API 降至 26 以下 - YukiHookAPI 将会尝试使用 [makeWorldReadable] 但仍有可能不成功 * - 未使用 LSPosed 环境请将你的模块 API 降至 26 以下 - [YukiHookAPI] 将会尝试使用 [makeWorldReadable] 但仍有可能不成功
* *
* - ❗当你在模块中存取数据的时候 [context] 必须不能是空的 * - ❗当你在模块中存取数据的时候 [context] 必须不能是空的
* *

View File

@@ -34,7 +34,7 @@ import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.encase import com.highcapable.yukihookapi.hook.factory.encase
/** /**
* YukiHookAPI 的 Xposed 装载 API 调用接口 * [YukiHookAPI] 的 Xposed 装载 API 调用接口
* *
* - ❗请在此类上添加注释 [InjectYukiHookWithXposed] 标记模块 Hook 入口 * - ❗请在此类上添加注释 [InjectYukiHookWithXposed] 标记模块 Hook 入口
* *