This commit is contained in:
2022-02-05 03:53:38 +08:00
parent 5c3335fab3
commit e6bfc181de
44 changed files with 349 additions and 216 deletions

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.highcapable.yukihookapi" />

View File

@@ -1 +0,0 @@
com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage

View File

@@ -1,97 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE")
package com.highcapable.yukihookapi
import android.content.pm.ApplicationInfo
import com.highcapable.yukihookapi.YukiHookAPI.encase
import com.highcapable.yukihookapi.annotation.DoNotUseField
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.param.CustomParam
import com.highcapable.yukihookapi.param.PackageParam
/**
* YukiHook 的装载 API 调用类
*
* 可以实现作为模块装载和自定义 Hook 装载两种方式
*
* 模块装载方式已经自动对接 Xposed API - 可直接调用 [encase] 完成操作
*/
object YukiHookAPI {
/** 全局标识 */
const val TAG = "YukiHookAPI"
/** Xposed Hook API 绑定的模块包名 */
@DoNotUseField
internal var modulePackageName = ""
/** Xposed Hook API 方法体回调 */
@DoNotUseField
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
/**
* 作为模块装载调用入口方法 - Xposed API
* @param moduleName 模块包名 - 填入当前的 BuildConfig.APPLICATION_ID
* @param initiate Hook 方法体
*/
fun encase(moduleName: String = "", initiate: PackageParam.() -> Unit) {
modulePackageName = moduleName
packageParamCallback = initiate
}
/**
* 作为模块装载调用入口方法 - Xposed API
* @param moduleName 模块包名 - 填入当前的 BuildConfig.APPLICATION_ID
* @param hooker Hook 子类数组 - 必填不能为空
* @throws IllegalStateException 如果 [hooker] 是空的
*/
fun encase(moduleName: String = "", vararg hooker: YukiBaseHooker) {
modulePackageName = moduleName
packageParamCallback = {
if (hooker.isNotEmpty())
hooker.forEach { it.assignInstance(packageParam = this) }
else error("Hooker is empty")
}
}
/**
* 自定义 Hook 方法装载入口
* @param classLoader [ClassLoader]
* @param packageName 包名
* @param appInfo [ApplicationInfo]
* @param initiate Hook 方法体
*/
fun encase(
classLoader: ClassLoader,
packageName: String,
appInfo: ApplicationInfo,
initiate: PackageParam.() -> Unit
) = initiate.invoke(PackageParam(customParam = CustomParam(classLoader, appInfo, packageName)))
}

View File

@@ -1,52 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
package com.highcapable.yukihookapi.annotation
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@MustBeDocumented
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.PROPERTY,
AnnotationTarget.FIELD,
AnnotationTarget.LOCAL_VARIABLE,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.TYPEALIAS
)
@Retention(AnnotationRetention.BINARY)
/**
* ⚠️ 警告方法外部调用声明
* 此方法除继承和接口外不应该在这里被调用
* 如果调用此方法可能会出现错误或 APP 发生异常
*/
annotation class DoNotUseField

View File

@@ -1,52 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
package com.highcapable.yukihookapi.annotation
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@MustBeDocumented
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.PROPERTY,
AnnotationTarget.LOCAL_VARIABLE,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.TYPEALIAS
)
@Retention(AnnotationRetention.BINARY)
/**
* ⚠️ 警告方法外部调用声明
* 此方法除继承和接口外不应该在这里被调用
* 如果调用此方法可能会出现错误或 APP 发生异常
*/
annotation class DoNotUseMethod

View File

@@ -1,51 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
package com.highcapable.yukihookapi.annotation
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@MustBeDocumented
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.PROPERTY,
AnnotationTarget.LOCAL_VARIABLE,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.TYPEALIAS
)
@Retention(AnnotationRetention.BINARY)
/**
* 暂时不需要(用的很少)的变量
* 可以打上此标记
*/
annotation class UseLessField

View File

@@ -1,51 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
package com.highcapable.yukihookapi.annotation
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@MustBeDocumented
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.PROPERTY,
AnnotationTarget.LOCAL_VARIABLE,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.TYPEALIAS
)
@Retention(AnnotationRetention.BINARY)
/**
* 暂时不需要(用的很少)的方法
* 可以打上此标记
*/
annotation class UseLessMethod

View File

@@ -1,45 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/3.
*/
package com.highcapable.yukihookapi.annotation.xposed
import androidx.annotation.Keep
import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy
import com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage
/**
* 标识注入 YukiHook 的类
*
* 此类将使用 [YukiHookLoadPackage] 自动调用 XposedInit
*
* 你可以将被注释的类继承于 [YukiHookXposedInitProxy] 接口实现 [YukiHookXposedInitProxy.onHook] 方法
*
* 只能拥有一个 Hook 入口 - 多个入口将以首个得到的入口为准
*/
@Target(AnnotationTarget.CLASS)
@Keep
annotation class InjectYukiHookWithXposed

View File

@@ -1,393 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE")
package com.highcapable.yukihookapi.hook.core
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
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.log.loggerE
import com.highcapable.yukihookapi.param.HookParam
import com.highcapable.yukihookapi.param.PackageParam
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XC_MethodReplacement
import de.robv.android.xposed.XposedBridge
import java.lang.reflect.Field
import java.lang.reflect.Member
/**
* YukiHook 核心类实现方法
*
* 这是一个 API 对接类 - 实现原生对接 [XposedBridge]
* @param packageParam 需要传入 [PackageParam] 实现方法调用
* @param hookClass 要 Hook 的 [Class]
*/
class YukiHookCreater(private val packageParam: PackageParam, val hookClass: Class<*>) {
/** 设置要 Hook 的方法、构造类 */
private var hookMembers = HashMap<String, MemberHookCreater>()
/**
* 注入要 Hook 的方法、构造类
* @param initiate 方法体
*/
fun injectMember(initiate: MemberHookCreater.() -> Unit) =
MemberHookCreater().apply(initiate).apply {
hookMembers[toString()] = this
}.create()
/**
* Hook 执行入口 - 不可在外部调用
* @throws IllegalStateException 如果必要参数没有被设置
*/
@DoNotUseMethod
fun hook() {
if (hookMembers.isEmpty()) error("Hook Members is empty,hook aborted")
hookMembers.forEach { (_, member) -> member.hook() }
}
/**
* 智能全局方法、构造类查找类实现方法
*
* 处理需要 Hook 的方法
*/
inner class MemberHookCreater {
/** [beforeHook] 回调 */
private var beforeHookCallback: (HookParam.() -> Unit)? = null
/** [afterHook] 回调 */
private var afterHookCallback: (HookParam.() -> Unit)? = null
/** [replaceAny]、[replaceUnit]、[replaceTo] 等回调 */
private var replaceHookCallback: (HookParam.() -> Any?)? = null
/** Hook 过程中出现错误回调 */
private var onConductFailureCallback: ((HookParam, Throwable) -> Unit)? = null
/** Hook 开始时出现错误回调 */
private var onHookingFailureCallback: ((Throwable) -> Unit)? = null
/** 当找不到方法、变量时错误回调 */
private var onNoSuchMemberCallback: ((Throwable) -> Unit)? = null
/** 全部错误回调 */
private var onAllFailureCallback: ((Throwable) -> Unit)? = null
/** 是否为替换 Hook 模式 */
private var isReplaceHookMode = false
/** 是否停止 Hook */
private var isStopHookMode = false
/**
* 手动指定要 Hook 的方法、构造类
*
* 你可以调用 [hookClass] 来手动查询要 Hook 的方法
*/
var member: Member? = null
/**
* 查找需要 Hook 的方法
*
* 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换
* @param initiate 方法体
*/
fun method(initiate: MethodFinder.() -> Unit) {
runCatching {
member = MethodFinder(hookClass).apply(initiate).find()
}.onFailure {
isStopHookMode = true
onNoSuchMemberCallback?.invoke(it)
onAllFailureCallback?.invoke(it)
if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(it)
}
}
/**
* 查找需要 Hook 的构造类
*
* 你只能使用一次 [method] 或 [constructor] 方法 - 否则结果会被替换
* @param initiate 方法体
*/
fun constructor(initiate: ConstructorFinder.() -> Unit) {
runCatching {
member = ConstructorFinder(hookClass).apply(initiate).find()
}.onFailure {
isStopHookMode = true
onNoSuchMemberCallback?.invoke(it)
onAllFailureCallback?.invoke(it)
if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(it)
}
}
/**
* 查找 [Field]
* @param initiate 方法体
* @return [FieldFinder.Result]
*/
fun HookParam.field(initiate: FieldFinder.() -> Unit) =
try {
FieldFinder(hookClass).apply(initiate).find()
} catch (e: Throwable) {
isStopHookMode = true
onNoSuchMemberCallback?.invoke(e)
onAllFailureCallback?.invoke(e)
if (onNoSuchMemberCallback == null && onAllFailureCallback == null) onHookFailureMsg(e)
FieldFinder(hookClass).Result()
}
/**
* 在方法执行完成前 Hook
*
* 不可与 [replaceAny]、[replaceUnit]、[replaceTo] 同时使用
* @param initiate [HookParam] 方法体
*/
fun beforeHook(initiate: HookParam.() -> Unit) {
isReplaceHookMode = false
beforeHookCallback = initiate
}
/**
* 在方法执行完成后 Hook
*
* 不可与 [replaceAny]、[replaceUnit]、[replaceTo] 同时使用
* @param initiate [HookParam] 方法体
*/
fun afterHook(initiate: HookParam.() -> Unit) {
isReplaceHookMode = false
afterHookCallback = initiate
}
/**
* 替换此方法内容 - 给出返回值
*
* 不可与 [beforeHook]、[afterHook] 同时使用
* @param initiate [HookParam] 方法体
*/
fun replaceAny(initiate: HookParam.() -> Any?) {
isReplaceHookMode = true
replaceHookCallback = initiate
}
/**
* 替换此方法内容 - 没有返回值 ([Unit])
*
* 不可与 [beforeHook]、[afterHook] 同时使用
* @param initiate [HookParam] 方法体
*/
fun replaceUnit(initiate: HookParam.() -> Unit) {
isReplaceHookMode = true
replaceHookCallback = initiate
}
/**
* 替换方法返回值
*
* 不可与 [beforeHook]、[afterHook] 同时使用
* @param any 要替换为的返回值对象
*/
fun replaceTo(any: Any?) {
isReplaceHookMode = true
replaceHookCallback = { any }
}
/**
* 替换方法返回值为 true
*
* 确保替换方法的返回对象为 [Boolean]
*
* 不可与 [beforeHook]、[afterHook] 同时使用
*/
fun replaceToTrue() {
isReplaceHookMode = true
replaceHookCallback = { true }
}
/**
* 替换方法返回值为 false
*
* 确保替换方法的返回对象为 [Boolean]
*
* 不可与 [beforeHook]、[afterHook] 同时使用
*/
fun replaceToFalse() {
isReplaceHookMode = true
replaceHookCallback = { false }
}
/**
* 拦截此方法
*
* 这将会禁止此方法执行并返回 null
*
* 不可与 [beforeHook]、[afterHook] 同时使用
*/
fun intercept() {
isReplaceHookMode = true
replaceHookCallback = { null }
}
/**
* Hook 创建入口 - 不可在外部调用
* @return [Result]
*/
@DoNotUseMethod
fun create() = Result()
/**
* Hook 执行入口 - 不可在外部调用
* @throws IllegalStateException 如果必要参数没有被设置
*/
@DoNotUseMethod
fun hook() {
if (isStopHookMode) return
member?.also { member ->
runCatching {
if (isReplaceHookMode)
XposedBridge.hookMethod(member, object : XC_MethodReplacement() {
override fun replaceHookedMethod(baseParam: MethodHookParam?): Any? {
if (baseParam == null) return null
return HookParam(baseParam).let { param ->
try {
replaceHookCallback?.invoke(param)
} catch (e: Throwable) {
onConductFailureCallback?.invoke(param, e)
onAllFailureCallback?.invoke(e)
if (onConductFailureCallback == null && onAllFailureCallback == null)
onHookFailureMsg(e)
null
}
}
}
})
else
XposedBridge.hookMethod(member, object : XC_MethodHook() {
override fun beforeHookedMethod(baseParam: MethodHookParam?) {
if (baseParam == null) return
HookParam(baseParam).also { param ->
runCatching {
beforeHookCallback?.invoke(param)
}.onFailure {
onConductFailureCallback?.invoke(param, it)
onAllFailureCallback?.invoke(it)
if (onConductFailureCallback == null && onAllFailureCallback == null)
onHookFailureMsg(it)
}
}
}
override fun afterHookedMethod(baseParam: MethodHookParam?) {
if (baseParam == null) return
HookParam(baseParam).also { param ->
runCatching {
afterHookCallback?.invoke(param)
}.onFailure {
onConductFailureCallback?.invoke(param, it)
onAllFailureCallback?.invoke(it)
if (onConductFailureCallback == null && onAllFailureCallback == null)
onHookFailureMsg(it)
}
}
}
})
}.onFailure {
onHookingFailureCallback?.invoke(it)
onAllFailureCallback?.invoke(it)
if (onHookingFailureCallback == null && onAllFailureCallback == null) onHookFailureMsg(it)
}
} ?: error("Hook Member cannot be null")
}
/**
* Hook 失败但未设置 [onAllFailureCallback] 将默认输出失败信息
* @param throwable 异常信息
*/
private fun onHookFailureMsg(throwable: Throwable) =
loggerE(msg = "Try to hook $hookClass[$member] got an Exception", e = throwable)
override fun toString() = "$member#YukiHook"
/**
* 监听 Hook 结果实现类
*
* 可在这里处理失败事件监听
*/
inner class Result {
/**
* 创建监听失败事件方法体
* @param initiate 方法体
* @return [Result] 可继续向下监听
*/
fun failures(initiate: Result.() -> Unit) = apply(initiate)
/**
* 监听 Hook 进行过程中发生错误的回调方法
* @param initiate 回调错误 - ([HookParam] 当前 Hook 实例,[Throwable] 异常)
* @return [Result] 可继续向下监听
*/
fun onConductFailure(initiate: (HookParam, Throwable) -> Unit): Result {
onConductFailureCallback = initiate
return this
}
/**
* 监听 Hook 开始时发生错误的回调方法
* @param initiate 回调错误 - ([Throwable] 异常)
* @return [Result] 可继续向下监听
*/
fun onHookingFailure(initiate: (Throwable) -> Unit): Result {
onHookingFailureCallback = initiate
return this
}
/**
* 监听 Hook 过程发生找不到方法、变量错误的回调方法
* @param initiate 回调错误 - ([Throwable] 异常)
* @return [Result] 可继续向下监听
*/
fun onNoSuchMemberFailure(initiate: (Throwable) -> Unit): Result {
onNoSuchMemberCallback = initiate
return this
}
/**
* 监听全部 Hook 过程发生错误的回调方法
* @param initiate 回调错误 - ([Throwable] 异常)
* @return [Result] 可继续向下监听
*/
fun onAllFailure(initiate: (Throwable) -> Unit): Result {
onAllFailureCallback = initiate
return this
}
}
}
}

View File

@@ -1,65 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/4.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import java.lang.reflect.Constructor
/**
* [Constructor] 查找类
*
* 可通过指定类型查找指定构造类
* @param hookClass 当前被 Hook 的 [Class]
*/
class ConstructorFinder(private val hookClass: Class<*>) {
/** 方法参数 */
private var params: Array<out Class<*>>? = null
/**
* 方法参数
* @param param 参数数组
*/
fun param(vararg param: Class<*>) {
params = param
}
/**
* 得到构造类 - 不能在外部调用
* @return [Constructor]
* @throws NoSuchMethodError 如果找不到构造类
*/
@DoNotUseMethod
fun find(): Constructor<*> =
if (params != null)
ReflectionUtils.findConstructorExact(hookClass, *params!!)
else ReflectionUtils.findConstructorExact(hookClass)
}

View File

@@ -1,94 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/4.
*/
@file:Suppress("unused", "UNCHECKED_CAST", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import java.lang.reflect.Field
/**
* Field 查找结果实现类
*
* 可在这里处理找到的 [fieldInstance]
* @param hookClass 当前被 Hook 的 [Class]
*/
class FieldFinder(private val hookClass: Class<*>) {
/** 当前找到的 [Field] */
private var fieldInstance: Field? = null
/** 变量名 */
var name = ""
/** 变量类型 */
var type: Class<*>? = null
/**
* 得到变量处理结果 - 不能在外部调用
* @return [Result]
* @throws NoSuchFieldError 如果找不到变量
*/
@DoNotUseMethod
fun find(): Result {
fieldInstance = when {
name.isBlank() -> error("Field name cannot be empty")
else -> ReflectionUtils.findFieldIfExists(hookClass, type?.name, name)
}
return Result()
}
/**
* Field 查找结果实现类
*
* 可在这里处理找到的 [fieldInstance]
*/
inner class Result {
/**
* 设置变量实例
* @param instance 变量所在的实例对象 - 如果是静态可不填 - 默认 null
* @param any 设置的实例内容
*/
fun set(instance: Any? = null, any: Any?) = give()?.set(instance, any)
/**
* 得到变量实例
* @param instance 变量所在的实例对象 - 如果是静态可不填 - 默认 null
* @return [T] or null
*/
fun <T> get(instance: Any? = null) = give()?.get(instance) as? T?
/**
* 得到变量本身
* @return [Field] or null
*/
fun give() = fieldInstance
}
}

View File

@@ -1,75 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/4.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.hook.core.finder
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.utils.ReflectionUtils
import java.lang.reflect.Method
/**
* [Method] 查找类
*
* 可通过指定类型查找指定方法
* @param hookClass 当前被 Hook 的 [Class]
*/
class MethodFinder(private val hookClass: Class<*>) {
/** 方法参数 */
private var params: Array<out Class<*>>? = null
/** 方法名 */
var name = ""
/** 方法返回值 */
var returnType: Class<*>? = null
/**
* 方法参数
* @param param 参数数组
*/
fun param(vararg param: Class<*>) {
params = param
}
/**
* 得到方法 - 不能在外部调用
* @return [Method]
* @throws NoSuchMethodError 如果找不到方法
*/
@DoNotUseMethod
fun find(): Method =
when {
name.isBlank() -> error("Method name cannot be empty")
else ->
if (params != null)
ReflectionUtils.findMethodBestMatch(hookClass, returnType, name, *params!!)
else ReflectionUtils.findMethodNoParam(hookClass, returnType, name)
}
}

View File

@@ -1,82 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/3.
*/
package com.highcapable.yukihookapi.hook.entity
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy
import com.highcapable.yukihookapi.param.PackageParam
/**
* YukiHook 的子类实现
*
* 也许你的 Module 中存在多个 Hooker - 继承此类可以方便帮你管理每个 Hooker
*
* 你可以继续继承此类进行自定义 Hooker 相关参数
*
* 你可以在 [YukiHookXposedInitProxy] 的 [YukiHookXposedInitProxy.onHook] 中实现如下用法
*
* 调用 [YukiHookAPI.encase] encase(moduleName = "模块包名", MainHooker(), SecondHooker(), ThirdHooker() ...)
*
* 调用 [PackageParam.loadHooker] loadHooker(hooker = CustomHooker())
*
* ....
*
* 继承类参考示例:
*
* ....
*
* class CustomHooker : YukiBaseHooker() {
*
* ....override fun onHook() {
*
* ........// Your code here.
*
* ....}
*
* }
*
* ....
*
* 详情请参考 [YukiHookAPI Wiki](https://github.com/fankes/YukiHookAPI/wiki)
*/
abstract class YukiBaseHooker : PackageParam() {
/**
* 赋值并克隆一个 [PackageParam]
* @param packageParam 需要使用的 [PackageParam]
*/
@DoNotUseMethod
internal fun assignInstance(packageParam: PackageParam) {
baseAssignInstance(packageParam)
onHook()
}
/** 子类 Hook 开始 */
abstract fun onHook()
}

View File

@@ -1,133 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.factory
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
/**
* 字符串转换为实体类
* @return [Class]
* @throws NoClassDefFoundError
*/
val String.clazz: Class<*> get() = Class.forName(this)
/**
* 查找方法是否存在
* @param name 名称
* @param clazz params
* @return [Boolean] 是否存在
*/
fun Class<*>.hasMethod(name: String, vararg clazz: Class<*>): Boolean =
try {
getDeclaredMethod(name, *clazz)
true
} catch (_: Throwable) {
false
}
/**
* 查找静态 [Field] 的实例
* @param name 名称
* @return [Any] 实例对象
* @throws NoSuchFieldError
*/
fun Class<*>.findStaticField(name: String): Any? = getDeclaredField(name).apply { isAccessible = true }[null]
/**
* 查找 [Field] 的实例 - 不能是静态
* @param any 对象
* @param name 名称
* @return [Any] 实例对象
* @throws NoSuchFieldError
*/
fun Class<*>.findField(any: Any?, name: String): Any? = getDeclaredField(name).apply { isAccessible = true }[any]
/**
* 设置 [Field] - 不能是静态
* @param any 对象
* @param name 名称
* @param value 值
* @throws NoSuchFieldError
*/
fun Class<*>.modifyField(any: Any?, name: String, value: Any?) {
getDeclaredField(name).apply {
isAccessible = true
set(any, value)
}
}
/**
* 查找目标变量
* @param name 方法名
* @return [Field]
* @throws NoSuchFieldError 如果找不到变量会报错
*/
fun Class<*>.findField(name: String): Field =
getDeclaredField(name).apply { isAccessible = true }
/**
* 得到方法
* @param name 方法名称
* @param clazz params
* @return [Method]
* @throws NoSuchMethodError
*/
fun Class<*>.findMethod(name: String, vararg clazz: Class<*>): Method? =
getDeclaredMethod(name, *clazz).apply { isAccessible = true }
/**
* 得到构造类
* @param parameterTypes params
* @return [Constructor]
* @throws NoSuchMethodError
*/
fun Class<*>.findConstructor(vararg parameterTypes: Class<*>?): Constructor<out Any>? =
getDeclaredConstructor(*parameterTypes).apply { isAccessible = true }
/**
* 执行方法 - 静态
* @param anys 方法参数
* @return [T]
* @throws IllegalStateException 如果 [T] 类型错误
*/
inline fun <reified T> Method.invokeStatic(vararg anys: Any) =
invoke(null, anys) as? T? ?: error("Method ReturnType cannot cast to ${T::class.java}")
/**
* 执行方法 - 非静态
* @param any 目标对象
* @param anys 方法参数
* @return [T]
* @throws IllegalStateException 如果 [T] 类型错误
*/
inline fun <reified T> Method.invokeAny(any: Any?, vararg anys: Any) =
invoke(any, anys) as? T? ?: error("Method ReturnType cannot cast to ${T::class.java}")

View File

@@ -1,83 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.factory
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy
import com.highcapable.yukihookapi.param.PackageParam
/**
* 在 [YukiHookXposedInitProxy] 中装载 [YukiHookAPI]
* @param moduleName 模块包名 - 填入当前的 BuildConfig.APPLICATION_ID
* @param initiate Hook 方法体
*/
fun YukiHookXposedInitProxy.encase(moduleName: String = "", initiate: PackageParam.() -> Unit) =
YukiHookAPI.encase(moduleName, initiate)
/**
* 在 [YukiHookXposedInitProxy] 中装载 [YukiHookAPI]
* @param moduleName 模块包名 - 填入当前的 BuildConfig.APPLICATION_ID
* @param hooker Hook 子类数组 - 必填不能为空
* @throws IllegalStateException 如果 [hooker] 是空的
*/
fun YukiHookXposedInitProxy.encase(moduleName: String = "", vararg hooker: YukiBaseHooker) =
YukiHookAPI.encase(moduleName, *hooker)
/**
* 判断模块是否在太极、无极中激活
* @return [Boolean] 是否激活
*/
val Context.isTaichiModuleActive: Boolean
get() {
var isModuleActive = false
runCatching {
var result: Bundle? = null
Uri.parse("content://me.weishu.exposed.CP/").also { uri ->
runCatching {
result = contentResolver.call(uri, "active", null, null)
}.onFailure {
// TaiChi is killed, try invoke
runCatching {
startActivity(Intent("me.weishu.exp.ACTION_ACTIVE").apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
}.onFailure { return false }
}
if (result == null)
result = contentResolver.call(Uri.parse("content://me.weishu.exposed.CP/"), "active", null, null)
if (result == null) return false
}
isModuleActive = result?.getBoolean("active", false) == true
}
return isModuleActive
}

View File

@@ -1,92 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/3.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.log
import android.util.Log
import com.highcapable.yukihookapi.YukiHookAPI
import de.robv.android.xposed.XposedBridge
/**
* 向控制台和 [XposedBridge] 打印日志 - D
*
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG]
* @param msg 日志打印的内容
*/
fun loggerD(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
Log.d(tag, msg)
XposedBridge.log("[$tag][D]--> $msg")
}
/**
* 向控制台和 [XposedBridge] 打印日志 - I
*
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG]
* @param msg 日志打印的内容
*/
fun loggerI(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
Log.i(tag, msg)
XposedBridge.log("[$tag][I]--> $msg")
}
/**
* 向控制台和 [XposedBridge] 打印日志 - W
*
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG]
* @param msg 日志打印的内容
*/
fun loggerW(tag: String = YukiHookAPI.TAG, msg: String) = runCatching {
Log.w(tag, msg)
XposedBridge.log("[$tag][W]--> $msg")
}
/**
* 向控制台和 [XposedBridge] 打印日志 - E
*
* 你可以对此方法进行二次封装
*
* [XposedBridge] 中的日志打印风格为 [[tag]]「类型」--> [msg]
* @param tag 日志打印的标签 - 建议和自己的模块名称设置成一样的 - 默认为 [YukiHookAPI.TAG]
* @param msg 日志打印的内容
* @param e 可填入异常堆栈信息 - 将自动完整打印到控制台
*/
fun loggerE(tag: String = YukiHookAPI.TAG, msg: String, e: Throwable? = null) = runCatching {
Log.e(tag, msg, e)
XposedBridge.log("[$tag][E]--> $msg")
e?.also { XposedBridge.log(it) }
}

View File

@@ -1,83 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.proxy
import androidx.annotation.Keep
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.encase
/**
* YukiHook 的 Xposed 装载 API 调用接口
*
* Hook 开始时将自动调用 [onHook] 方法
*
* 请在 [onHook] 中调用 [YukiHookAPI.encase] 或直接调用 [encase]
*
* 可写作如下形式:
*
* ....
*
* override fun onHook() = YukiHookAPI.encase(moduleName = "模块包名") {
*
* ....// Your code here.
*
* }
*
* ....
*
* 还可写作如下形式:
*
* ....
*
* override fun onHook() = encase(moduleName = "模块包名") {
*
* ....// Your code here.
*
* }
*
* ....
*
* 若你喜欢分类创建 Hooker - 还可以这样写:
*
* ......
*
* override fun onHook() = encase(moduleName = "模块包名", MainHooker(), SecondHooker(), ThirdHooker() ...)
*
* ......
*
* 详情请参考 [YukiHookAPI Wiki](https://github.com/fankes/YukiHookAPI/wiki)
*/
@Keep
interface YukiHookXposedInitProxy {
/** 模块装载调用入口方法 - Xposed API */
@Keep
fun onHook()
}

View File

@@ -1,80 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.type
import android.app.Activity
import android.app.Application
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.res.Resources
import android.os.Bundle
/**
* 获得 [Context] 类型
* @return [Class]
*/
val ContextClass get() = Context::class.java
/**
* 获得 [Activity] 类型
* @return [Class]
*/
val ActivityClass get() = Activity::class.java
/**
* 获得 [Service] 类型
* @return [Class]
*/
val ServiceClass get() = Service::class.java
/**
* 获得 [BroadcastReceiver] 类型
* @return [Class]
*/
val BroadcastReceiverClass get() = BroadcastReceiver::class.java
/**
* 获得 [Bundle] 类型
* @return [Class]
*/
val BundleClass get() = Bundle::class.java
/**
* 获得 [Resources] 类型
* @return [Class]
*/
val ResourcesClass get() = Resources::class.java
/**
* 获得 [Application] 类型
* @return [Class]
*/
val ApplicationClass get() = Application::class.java

View File

@@ -1,104 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.type
import java.io.Serializable
/**
* 获得 [Any] 类型
* @return [Class]
*/
val AnyType get() = Any::class.java
/**
* 获得 [Unit] 类型
* @return [Class]
*/
val UnitType get() = Unit::class.javaPrimitiveType
/**
* 获得 [Boolean] 类型
* @return [Class]
*/
val BooleanType get() = Boolean::class.javaPrimitiveType
/**
* 获得 [Int] 类型
* @return [Class]
*/
val IntType get() = Int::class.javaPrimitiveType
/**
* 获得 [Long] 类型
* @return [Class]
*/
val LongType get() = Long::class.javaPrimitiveType
/**
* 获得 [Short] 类型
* @return [Class]
*/
val ShortType get() = Short::class.javaPrimitiveType
/**
* 获得 [Float] 类型
* @return [Class]
*/
val FloatType get() = Float::class.javaPrimitiveType
/**
* 获得 [Double] 类型
* @return [Class]
*/
val DoubleType get() = Double::class.javaPrimitiveType
/**
* 获得 [String] 类型
* @return [Class]
*/
val StringType get() = String::class.java
/**
* 获得 [Char] 类型
* @return [Class]
*/
val CharType get() = Char::class.java
/**
* 获得 [CharSequence] 类型
* @return [Class]
*/
val CharSequenceType get() = CharSequence::class.java
/**
* 获得 [Serializable] 类型
* @return [Class]
*/
val SerializableClass get() = Serializable::class.java

View File

@@ -1,82 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused")
package com.highcapable.yukihookapi.hook.type
import android.view.View
import android.view.ViewGroup
import android.widget.*
/**
* 获得 [View] 类型
* @return [Class]
*/
val ViewClass get() = View::class.java
/**
* 获得 [ViewGroup] 类型
* @return [Class]
*/
val ViewGroupClass get() = ViewGroup::class.java
/**
* 获得 [TextView] 类型
* @return [Class]
*/
val TextViewClass get() = TextView::class.java
/**
* 获得 [ImageView] 类型
* @return [Class]
*/
val ImageViewClass get() = ImageView::class.java
/**
* 获得 [EditText] 类型
* @return [Class]
*/
val EditTextClass get() = EditText::class.java
/**
* 获得 [Button] 类型
* @return [Class]
*/
val ButtonClass get() = Button::class.java
/**
* 获得 [CheckBox] 类型
* @return [Class]
*/
val CheckBoxClass get() = CheckBox::class.java
/**
* 获得 [CompoundButton] 类型
* @return [Class]
*/
val CompoundButtonClass get() = CompoundButton::class.java

View File

@@ -1,243 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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 java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import de.robv.android.xposed.XposedHelpers;
@SuppressWarnings("ALL")
public class ReflectionUtils {
private static final HashMap<String, Field> fieldCache = new HashMap<>();
private static final HashMap<String, Method> methodCache = new HashMap<>();
private static String getParametersString(Class<?>... clazzes) {
StringBuilder sb = new StringBuilder("(");
boolean first = true;
for (Class<?> clazz : clazzes) {
if (first)
first = false;
else
sb.append(",");
if (clazz != null)
sb.append(clazz.getCanonicalName());
else
sb.append("null");
}
sb.append(")");
return sb.toString();
}
@Deprecated
public static <T> T getStaticObjectIfExists(Class<?> clazz, Class<?> fieldType, String fieldName) {
return getObjectIfExists(clazz, fieldType, fieldName, null);
}
public static <T> T getObjectIfExists(Class<?> clazz, Class<?> fieldType, String fieldName, Object obj) {
return getObjectIfExists(clazz, fieldType.getName(), fieldName, obj);
}
public static <T> T getObjectIfExists(Class<?> clazz, String typeName, String fieldName, Object obj) {
try {
Field field = findFieldIfExists(clazz, typeName, fieldName);
return field == null ? null : (T) field.get(obj);
} catch (Exception e) {
return null;
}
}
@Deprecated
public static void setStaticObjectField(Class<?> clazz, Class<?> fieldType, String fieldName, Object value)
throws NoSuchFieldException, IllegalAccessException {
findFieldIfExists(clazz, fieldType, fieldName).set(null, value);
}
@Deprecated
public static void setObjectField(Object obj, Class<?> fieldType, String fieldName, Object value)
throws NoSuchFieldException, IllegalAccessException {
if (obj != null) {
Field field = findFieldIfExists(obj.getClass(), fieldType, fieldName);
if (field != null) {
field.set(obj, value);
}
}
}
public static Field findFieldIfExists(Class<?> clazz, Class<?> fieldType, String fieldName)
throws NoSuchFieldException {
return findFieldIfExists(clazz, fieldType.getName(), fieldName);
}
public static boolean isCallingFrom(String className) {
StackTraceElement[] stackTraceElements = Thread.currentThread()
.getStackTrace();
for (StackTraceElement element : stackTraceElements) {
if (element.getClassName()
.contains(className)) {
return true;
}
}
return false;
}
public static boolean isCallingFromEither(String... classname) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement element : stackTraceElements) {
for (String name : classname) {
if (element.toString().contains(name)) {
return true;
}
}
}
return false;
}
public static Field findField(Class<?> clazz, String typeName, String fieldName) {
try {
return findFieldIfExists(clazz, typeName, fieldName);
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException("Can't find field <" + clazz.getName() + "#" + typeName + "#" + fieldName + ">");
}
}
public static Field findFieldIfExists(Class<?> clazz, String typeName, String fieldName)
throws NoSuchFieldException {
String fullFieldName = clazz.getName() + "#" + fieldName + "#" + typeName;
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(clazz.getName() + "#" + typeName + " " + fieldName);
}
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 方法名
*/
public static Method findMethodNoParam(Class<?> clazz, Class<?> returnType, String methodName) {
String fullMethodName = clazz.getName() + '#' + methodName + returnType + "#exact#NoParam";
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 方法参数类型数组
*/
public static Method findMethodBestMatch(Class<?> clazz, Class<?> returnType, String methodName, Class<?>... parameterTypes) {
String fullMethodName = clazz.getName() + '#' + methodName + returnType + getParametersString(parameterTypes) + "#exact";
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 构造类方法参数类型数组
*/
public static Constructor<?> findConstructorExact(Class<?> clazz, Class<?>... parameterTypes) {
String fullConstructorName = clazz.getName() + getParametersString(parameterTypes) + "#exact";
try {
Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError("Can't find constructor " + fullConstructorName);
}
}
private static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#zYukiHook#exact";
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError("Can't find 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 = XposedHelpers.findMethodsByExactParameters(clazz, returnType, parameterTypes);
for (Method method : methods) if (method.getName().equals(methodName)) return method;
} while ((clz = clz.getSuperclass()) != null);
}
throw new IllegalArgumentException("Method not found <name:" + methodName + " returnType:" + returnType.getName() + " paramType:" + getParametersString(parameterTypes) + "> in Class <" + clazz.getName() + ">");
}
}

View File

@@ -1,90 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
package com.highcapable.yukihookapi.hook.xposed
import androidx.annotation.Keep
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.clazz
import com.highcapable.yukihookapi.hook.factory.findConstructor
import com.highcapable.yukihookapi.hook.factory.findMethod
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy
import com.highcapable.yukihookapi.hook.type.BooleanType
import com.highcapable.yukihookapi.param.PackageParam
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.callbacks.XC_LoadPackage
/**
* 接管 Xposed 的 [IXposedHookLoadPackage] 入口
*
* 你可以使用 [YukiHookAPI.encase] 或在 [YukiHookXposedInitProxy] 中监听模块开始装载
*
* 需要标识 Hook 入口的类 - 请声明注释 [InjectYukiHookWithXposed]
*/
@Keep
class YukiHookLoadPackage : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
if (lpparam == null) return
runCatching {
/** 执行入口方法 */
hookEntryClassName().clazz.apply {
findMethod(name = "onHook")?.invoke(findConstructor()?.newInstance())
}
}.onFailure {
loggerE(msg = "Try to load ${hookEntryClassName()} Failed", e = it)
}
/** 装载 APP Hook 实体类 */
PackageParam(lpparam).apply {
/** Hook 模块激活状态 */
loadApp(name = YukiHookAPI.modulePackageName) {
YukiHookModuleStatus::class.java.hook {
injectMember {
method {
name = "isActive"
returnType = BooleanType
}
replaceToTrue()
}.onAllFailure { loggerE(msg = "Try to Hook ModuleStatus Failed", e = it) }
}
}
/** 设置装载回调 */
YukiHookAPI.packageParamCallback?.invoke(this)
}
}
/**
* 获得目标装载类名 - 通过 APT 自动设置 TODO 待实现
* @return [String] 目标装载类名
*/
@Keep
private fun hookEntryClassName() = "com.highcapable.yukihookapi.demo.hook.inject.MainInjecter"
}

View File

@@ -1,51 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/3.
*/
package com.highcapable.yukihookapi.hook.xposed
import androidx.annotation.Keep
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus.isActive
/**
* 这是一个 Xposed 模块 Hook 状态类
*
* 我们需要监听自己的模块是否被激活 - 可直接调用这个类的 [isActive] 方法
*/
@Keep
object YukiHookModuleStatus {
/**
* 此方法经过 Hook 后返回 true 即模块已激活
* @return [Boolean]
*/
@Keep
fun isActive(): Boolean {
loggerI(msg = "This Module is not actived")
return false
}
}

View File

@@ -1,42 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
package com.highcapable.yukihookapi.param
import android.content.pm.ApplicationInfo
/**
* 自定义 [PackageParam] 的装载入口置换类
* @param appClassLoader APP [ClassLoader]
* @param appInfo APP [ApplicationInfo]
* @param packageName 包名
*/
class CustomParam(
var appClassLoader: ClassLoader,
var appInfo: ApplicationInfo,
var packageName: String
)

View File

@@ -1,178 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.highcapable.yukihookapi.param
import de.robv.android.xposed.XC_MethodHook
import java.lang.reflect.Constructor
import java.lang.reflect.Method
/**
* Hook 方法、构造类的目标对象实现类
* @param baseParam 对接 Xposed API 的 [XC_MethodHook.MethodHookParam]
*/
class HookParam(private val baseParam: XC_MethodHook.MethodHookParam) {
/**
* 获取当前 [method] or [constructor] 的参数对象数组
* @return [Array]
*/
val args get() = baseParam.args ?: arrayOf(0)
/**
* 获取当前 [method] or [constructor] 的参数对象数组第一位
* @return [Array]
* @throws IllegalStateException 如果数组为空或对象为空
*/
val firstArgs get() = args[0] ?: error("HookParam args[0] with a non-null object")
/**
* 获取当前 [method] or [constructor] 的参数对象数组最后一位
* @return [Array]
* @throws IllegalStateException 如果数组为空或对象为空
*/
val lastArgs get() = args[args.lastIndex] ?: error("HookParam args[lastIndex] with a non-null object")
/**
* 获取当前 Hook 实例的对象
* @return [Any]
* @throws IllegalStateException 如果对象为空
*/
val instance get() = baseParam.thisObject ?: error("HookParam must with a non-null object")
/**
* 获取当前 Hook 实例的类对象
* @return [Class]
*/
val instanceClass get() = instance.javaClass
/**
* 获取当前 Hook 的方法
* @return [Method]
* @throws IllegalStateException 如果方法为空或方法类型不是 [Method]
*/
val method get() = baseParam.method as? Method? ?: error("Current hook method type is wrong or null")
/**
* 获取当前 Hook 的构造方法
* @return [Constructor]
* @throws IllegalStateException 如果方法为空或方法类型不是 [Constructor]
*/
val constructor get() = baseParam.method as? Constructor<*>? ?: error("Current hook constructor type is wrong or null")
/**
* 获取、设置当前 [method] or [constructor] 的返回值
* @return [Any] or null
*/
var result: Any?
get() = baseParam.result
set(value) {
baseParam.result = value
}
/**
* 获取当前 Hook 实例的对象 [T]
* @return [T]
* @throws IllegalStateException 如果对象为空或对象类型不是 [T]
*/
inline fun <reified T> instance() = instance as? T? ?: error("HookParam object cannot cast to ${T::class.java.name}")
/**
* 获取当前 [method] or [constructor] 的参数实例化对象类
* @param index 参数对象数组下标 - 默认是 0
* @return [ArgsModifyer]
*/
fun args(index: Int = 0) = ArgsModifyer(index)
/**
* 设置 [result] 返回值为 true
*
* 请确保返回值类型为 [Boolean]
*/
fun resultTrue() {
result = true
}
/**
* 设置 [result] 返回值为 false
*
* 请确保返回值类型为 [Boolean]
*/
fun resultFalse() {
result = false
}
/**
* 设置返回值为 null
*
* 此方法将强制设置方法体的 [result] 为 null
*/
fun resultNull() {
result = null
}
/**
* 对方法参数的修改进行实例化类
* @param index 参数对象数组下标
*/
inner class ArgsModifyer(private val index: Int) {
/**
* 设置方法参数的实例对象
* @param any 实例对象
* @throws IllegalStateException 如果目标方法参数对象数组为空或 [index] 下标不存在
*/
fun <T> set(any: T?) {
if (args.isEmpty()) error("HookParam method args is empty,mabe not has args")
if (index > args.lastIndex) error("HookParam method args index out of bounds,max is ${args.lastIndex}")
baseParam.args[index] = any
}
/**
* 设置方法参数的实例对象为 null
*
* 此方法可以将任何被 Hook 的目标对象设置为空
*/
fun setNull() = set(null)
/**
* 设置方法参数的实例对象为 true
*
* 请确保目标对象的类型是 [Boolean] 不然会发生意想不到的问题
*/
fun setTrue() = set(true)
/**
* 设置方法参数的实例对象为 false
*
* 请确保目标对象的类型是 [Boolean] 不然会发生意想不到的问题
*/
fun setFalse() = set(false)
}
}

View File

@@ -1,139 +0,0 @@
/**
* MIT License
*
* Copyright (C) 2022 HighCapable
*
* This file is part of YukiHookAPI.
*
* 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/2/2.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_API_USAGE")
package com.highcapable.yukihookapi.param
import android.content.pm.ApplicationInfo
import com.highcapable.yukihookapi.annotation.DoNotUseMethod
import com.highcapable.yukihookapi.hook.core.YukiHookCreater
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import de.robv.android.xposed.callbacks.XC_LoadPackage
/**
* 装载 Hook 的目标 APP 入口对象实现类
*
* 如果你想将 YukiHook 作为 Hook API 使用 - 你可自定义 [customParam]
*
* ⚠️ 特别注意如果 [baseParam] 和 [customParam] 都为空将发生问题
* @param baseParam 对接 Xposed API 的 [XC_LoadPackage.LoadPackageParam] - 默认空
* @param customParam 自定义装载类 - 默认空
*/
open class PackageParam(
private var baseParam: XC_LoadPackage.LoadPackageParam? = null,
private var customParam: CustomParam? = null
) {
/**
* 获取当前 APP 的 [ClassLoader]
* @return [ClassLoader]
* @throws IllegalStateException 如果 [ClassLoader] 是空的
*/
val appClassLoader
get() = baseParam?.classLoader ?: customParam?.appClassLoader ?: javaClass.classLoader
?: error("PackageParam ClassLoader is null")
/**
* 获取当前 APP 的 [ApplicationInfo]
* @return [ApplicationInfo]
*/
val appInfo get() = baseParam?.appInfo ?: customParam?.appInfo ?: ApplicationInfo()
/**
* 获取当前 APP 的进程名称
*
* 默认的进程名称是 [packageName] 如果自定义了 [customParam] 将返回包名
* @return [String]
*/
val processName get() = baseParam?.processName ?: customParam?.packageName ?: ""
/**
* 获取当前 APP 的包名
* @return [String]
*/
val packageName get() = baseParam?.packageName ?: customParam?.packageName ?: ""
/**
* 获取当前 APP 是否为第一个 Application
*
* 若自定义了 [customParam] 将永远返回 true
* @return [Boolean]
*/
val isFirstApplication get() = baseParam?.isFirstApplication ?: true
/**
* 赋值并克隆另一个 [PackageParam]
* @param another 另一个 [PackageParam]
*/
@DoNotUseMethod
internal fun baseAssignInstance(another: PackageParam) {
this.baseParam = another.baseParam
this.customParam = another.customParam
}
/**
* 装载并 Hook 指定包名的 APP
* @param name 包名
* @param initiate 方法体
*/
fun loadApp(name: String, initiate: PackageParam.() -> Unit) {
if (packageName == name) initiate(this)
}
/**
* 装载 Hook 子类
*
* 你可以在 Hooker 中继续装载 Hooker
* @param hooker Hook 子类
*/
fun loadHooker(hooker: YukiBaseHooker) = hooker.assignInstance(packageParam = this)
/**
* 将目标 [Class] 绑定到 [appClassLoader]
*
* ⚠️ 请注意未绑定到 [appClassLoader] 的 [Class] 不能被装载 - 调用 [hook] 方法会自动绑定
* @return [Class]
* @throws NoClassDefFoundError 如果找不到类会报错
*/
fun Class<*>.bind(): Class<*> = appClassLoader.loadClass(name)
/**
* 通过 [appClassLoader] 查询并装载 [Class]
* @param name 类名
* @return [Class]
* @throws NoClassDefFoundError 如果找不到类会报错
*/
fun findClass(name: String): Class<*> = appClassLoader.loadClass(name)
/**
* Hook 方法、构造类
* @param initiate 方法体
*/
fun Class<*>.hook(initiate: YukiHookCreater.() -> Unit) =
YukiHookCreater(packageParam = this@PackageParam, hookClass = bind()).apply(initiate).hook()
}