From 251b2053bdb52290b730889081f39d5c2460ca1d Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Wed, 2 Feb 2022 03:27:44 +0800 Subject: [PATCH] ... --- .../yukihookapi/demo/MainActivity.kt | 23 ++++++ .../com/highcapable/yukihookapi/demo/Yuu.kt | 2 +- yukihookapi/src/main/assets/xposed_init | 2 +- .../highcapable/yukihookapi/hook/YukiHook.kt | 75 ++++++++++++++++++ .../hook/{ => core}/YukiHookCreater.kt | 2 +- .../YukiHookFactory.kt} | 41 +++++----- .../{init => xposed}/YukiHookLoadPackage.kt | 10 ++- .../CustomParam.kt} | 12 ++- .../yukihookapi/param/PackageParam.kt | 78 +++++++++++++++---- 9 files changed, 203 insertions(+), 42 deletions(-) create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt rename yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/{ => core}/YukiHookCreater.kt (97%) rename yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/{helper/YukiHookHelper.kt => factory/YukiHookFactory.kt} (64%) rename yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/{init => xposed}/YukiHookLoadPackage.kt (83%) rename yukihookapi/src/main/java/com/highcapable/yukihookapi/{hook/factory/ReflectFactory.kt => param/CustomParam.kt} (77%) diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt index ca015a82..2702ce1f 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt @@ -29,11 +29,34 @@ package com.highcapable.yukihookapi.demo import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import com.highcapable.yukihookapi.hook.YukiHook +import com.highcapable.yukihookapi.hook.factory.yukiHook class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + YukiHook.encase { + optApp(name = "android") { + classOf(name = "a.b.c").hook { + + } + classOf(name = "a.b.c").hook { + + } + } + optApp(name = "bndroid") { + classOf(name = "a.b.c").hook { + + } + classOf(name = "a.b.c").hook { + + } + } + } + yukiHook { + + } } } \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt index 3107dcad..460698f5 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt @@ -1,6 +1,6 @@ package com.highcapable.yukihookapi.demo -import com.highcapable.yukihookapi.hook.init.YukiHookLoadPackage +import com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage class Yuu : YukiHookLoadPackage() { diff --git a/yukihookapi/src/main/assets/xposed_init b/yukihookapi/src/main/assets/xposed_init index 1d697fdd..37941abb 100644 --- a/yukihookapi/src/main/assets/xposed_init +++ b/yukihookapi/src/main/assets/xposed_init @@ -1 +1 @@ -com.highcapable.yukihookapi.hook.init.YukiHookLoadPackage \ No newline at end of file +com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt new file mode 100644 index 00000000..2610a8a8 --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt @@ -0,0 +1,75 @@ +/** + * 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") + +package com.highcapable.yukihookapi.hook + +import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.hook.YukiHook.encase +import com.highcapable.yukihookapi.param.CustomParam +import com.highcapable.yukihookapi.param.PackageParam + +/** + * YukiHook 的装载 API 调用类 + * 可以实现作为模块装载和自定义 Hook 装载两种方式 + * 模块装载方式已经自动对接 Xposed API - 可直接调用 [encase] 完成操作 + */ +object YukiHook { + + /** Xposed Hook API 方法体回调 */ + internal var packageParamCallback: (PackageParam.() -> Unit)? = null + + /** YukiHook 的 API 只能装载一次 */ + private var isLoaded = false + + /** + * 自身作为模块装载调用入口方法 - Xposed API + * ⚠️ 注意:只能装载一次 + * @param initiate Hook 方法体 + * @throws IllegalStateException 重复调用会抛出异常 + */ + fun encase(initiate: PackageParam.() -> Unit) { + if (isLoaded) error("YukiHook API already loaded") + isLoaded = true + packageParamCallback = initiate + } + + /** + * 自定义 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(customInstance = CustomParam(classLoader, appInfo, packageName))) +} \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHookCreater.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt similarity index 97% rename from yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHookCreater.kt rename to yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt index 0fdb6a9c..708864c8 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHookCreater.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt @@ -25,7 +25,7 @@ * * This file is Created by fankes on 2022/2/2. */ -package com.highcapable.yukihookapi.hook +package com.highcapable.yukihookapi.hook.core import com.highcapable.yukihookapi.param.HookParam import com.highcapable.yukihookapi.param.PackageParam diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/helper/YukiHookHelper.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt similarity index 64% rename from yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/helper/YukiHookHelper.kt rename to yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt index dc694b21..e591aa29 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/helper/YukiHookHelper.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt @@ -25,28 +25,31 @@ * * This file is Created by fankes on 2022/2/2. */ -package com.highcapable.yukihookapi.hook.helper +@file:Suppress("unused") +package com.highcapable.yukihookapi.hook.factory + +import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.hook.YukiHook import com.highcapable.yukihookapi.param.PackageParam /** - * YukiHook 的装载 API 调用类 - * 可以实现作为模块装载和 Hook 自身 APP 两种方式 + * Lambda 方式装载 [YukiHook] + * @param initiate Hook 方法体 */ -object YukiHookHelper { +fun yukiHook(initiate: PackageParam.() -> Unit) = YukiHook.encase(initiate) - /** - * 自身作为模块装载调用入口方法 - * @param initiate Hook 方法体 - */ - fun onHookApp(initiate: PackageParam.() -> Unit) { - - } - - /** - * 作为侵入式 Hook 自身装载调用入口方法 - * 正在开发敬请期待。 - * @param initiate Hook 方法体 - */ - fun onHookSelf(initiate: PackageParam.() -> Unit) {} -} \ No newline at end of file +/** + * Lambda 方式装载 [YukiHook] + * 自定义 Hook 方法装载入口 + * @param classLoader [ClassLoader] + * @param packageName 包名 + * @param appInfo [ApplicationInfo] + * @param initiate Hook 方法体 + */ +fun yukiHook( + classLoader: ClassLoader, + packageName: String, + appInfo: ApplicationInfo, + initiate: PackageParam.() -> Unit +) = YukiHook.encase(classLoader, packageName, appInfo, initiate) \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/init/YukiHookLoadPackage.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt similarity index 83% rename from yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/init/YukiHookLoadPackage.kt rename to yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt index be8b86af..7ef8af9f 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/init/YukiHookLoadPackage.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt @@ -25,22 +25,24 @@ * * This file is Created by fankes on 2022/2/2. */ -package com.highcapable.yukihookapi.hook.init +package com.highcapable.yukihookapi.hook.xposed import androidx.annotation.Keep -import com.highcapable.yukihookapi.hook.helper.YukiHookHelper +import com.highcapable.yukihookapi.hook.YukiHook +import com.highcapable.yukihookapi.param.PackageParam import de.robv.android.xposed.IXposedHookLoadPackage import de.robv.android.xposed.callbacks.XC_LoadPackage /** * 接管 Xposed 的 [IXposedHookLoadPackage] 入口 - * 你可以使用 [YukiHookHelper] 来监听模块开始装载 + * 你可以使用 [YukiHook] 来监听模块开始装载 */ @Keep class YukiHookLoadPackage : IXposedHookLoadPackage { override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { if (lpparam == null) return - + /** 设置装载回调 */ + YukiHook.packageParamCallback?.invoke(PackageParam(lpparam)) } } \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/ReflectFactory.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/CustomParam.kt similarity index 77% rename from yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/ReflectFactory.kt rename to yukihookapi/src/main/java/com/highcapable/yukihookapi/param/CustomParam.kt index 2bfc85e5..cc04136b 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/ReflectFactory.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/CustomParam.kt @@ -25,8 +25,14 @@ * * This file is Created by fankes on 2022/2/2. */ -package com.highcapable.yukihookapi.hook.factory +package com.highcapable.yukihookapi.param -import com.highcapable.yukihookapi.hook.YukiHookCreater +import android.content.pm.ApplicationInfo -fun Class<*>.hook(it: YukiHookCreater.() -> Unit) = YukiHookCreater().apply(it).hook() \ No newline at end of file +/** + * 自定义 [PackageParam] 的装载入口置换类 + * @param appClassLoader APP [ClassLoader] + * @param appInfo APP [ApplicationInfo] + * @param packageName 包名 + */ +class CustomParam(var appClassLoader: ClassLoader, var appInfo: ApplicationInfo, var packageName: String) \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt index b9d6f03d..6001e00f 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt @@ -1,35 +1,63 @@ +/** + * 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 android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.hook.core.YukiHookCreater import de.robv.android.xposed.callbacks.XC_LoadPackage /** * 装载 Hook 的目标 APP 入口对象实现类 * 如果是侵入式 Hook 自身 APP 可将参数 [instance] 置空获得当前类的 [ClassLoader] - * @param instance 对接 Xposed API 的 [XC_LoadPackage.LoadPackageParam] + * @param instance 对接 Xposed API 的 [XC_LoadPackage.LoadPackageParam] - 默认空 + * @param customInstance 自定义装载类 - 默认空 */ -class PackageParam(private val instance: XC_LoadPackage.LoadPackageParam? = null) { - - /** 当前 classLoader */ - private var privateClassLoader = instance?.classLoader ?: javaClass.classLoader +class PackageParam( + private val instance: XC_LoadPackage.LoadPackageParam? = null, + private val customInstance: CustomParam? = null +) { /** - * 获取、设置当前 APP 的 [ClassLoader] + * 获取当前 APP 的 [ClassLoader] * @return [ClassLoader] + * @throws IllegalStateException 如果 ClassLoader 是空的 */ - var appClassLoader: ClassLoader - get() = privateClassLoader - set(value) { - privateClassLoader = value - } + val appClassLoader + get() = instance?.classLoader ?: customInstance?.appClassLoader ?: javaClass.classLoader + ?: error("PackageParam ClassLoader is null") /** * 获取当前 APP 的 [ApplicationInfo] * @return [ApplicationInfo] */ - val appInfo get() = instance?.appInfo ?: ApplicationInfo() + val appInfo get() = instance?.appInfo ?: customInstance?.appInfo ?: ApplicationInfo() /** * 获取当前 APP 的进程名称 @@ -42,11 +70,35 @@ class PackageParam(private val instance: XC_LoadPackage.LoadPackageParam? = null * 获取当前 APP 的包名 * @return [String] */ - val packageName get() = instance?.packageName ?: "" + val packageName get() = instance?.packageName ?: customInstance?.packageName ?: "" /** * 获取当前 APP 是否为第一个 Application * @return [Boolean] */ val isFirstApplication get() = instance?.isFirstApplication ?: true + + /** + * Hook 指定包名的 APP + * @param name 包名 + * @param initiate 方法体 + */ + fun optApp(name: String, initiate: PackageParam.() -> Unit) { + if (packageName == name) initiate(this) + } + + /** + * 通过字符串装载 [Class] + * @param name 类名 + * @return [Class] + * @throws NoClassDefFoundError 如果找不到类会报错 + */ + fun classOf(name: String): Class<*> = appClassLoader.loadClass(name) + + /** + * Hook 方法、构造类 + * @param initiate 方法体 + */ + fun Class<*>.hook(initiate: YukiHookCreater.() -> Unit) = + YukiHookCreater(param = this@PackageParam).apply(initiate).hook() } \ No newline at end of file