From 9c0ec20b90afea1c6e2f6bca1cd2a9ae2b33163d Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Wed, 2 Feb 2022 05:44:17 +0800 Subject: [PATCH] ... --- .idea/jarRepositories.xml | 40 ++++++ .../yukihookapi/demo/MainActivity.kt | 32 ++--- .../com/highcapable/yukihookapi/demo/Yuu.kt | 9 -- .../yukihookapi/demo/hook/HookMain.kt | 55 ++++++++ build.gradle | 30 +++- settings.gradle | 14 -- yukihookapi/build.gradle | 2 +- .../yukihookapi/annotation/DoNotUseField.kt | 25 ++++ .../yukihookapi/annotation/DoNotUseMethod.kt | 25 ++++ .../yukihookapi/annotation/UseLessField.kt | 24 ++++ .../yukihookapi/annotation/UseLessMethod.kt | 24 ++++ .../highcapable/yukihookapi/hook/YukiHook.kt | 13 +- .../yukihookapi/hook/core/YukiHookCreater.kt | 128 ++++++++++++++++-- .../hook/factory/YukiHookFactory.kt | 21 +-- .../hook/proxy/YukiHookInitializeProxy.kt | 48 +++++++ .../hook/type/ComponentTypeFactory.kt | 80 +++++++++++ .../hook/type/VariableTypeFactory.kt | 98 ++++++++++++++ .../yukihookapi/hook/type/ViewTypeFactory.kt | 82 +++++++++++ .../hook/xposed/YukiHookLoadPackage.kt | 21 ++- .../yukihookapi/param/PackageParam.kt | 25 +++- 20 files changed, 704 insertions(+), 92 deletions(-) create mode 100644 .idea/jarRepositories.xml delete mode 100644 app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt create mode 100644 app/src/main/java/com/highcapable/yukihookapi/demo/hook/HookMain.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseField.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseMethod.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessField.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessMethod.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookInitializeProxy.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ComponentTypeFactory.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt create mode 100644 yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ViewTypeFactory.kt diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..3615609d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 2702ce1f..fdc4a13c 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/MainActivity.kt @@ -28,35 +28,21 @@ package com.highcapable.yukihookapi.demo import android.os.Bundle +import androidx.appcompat.app.AlertDialog 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 { - - } + // for test + AlertDialog.Builder(this) + .setMessage(hello()) + .setPositiveButton("OK", null) + .show() } + + // for test + private fun hello() = "正常显示的一行文字" } \ 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 deleted file mode 100644 index 460698f5..00000000 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/Yuu.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.highcapable.yukihookapi.demo - -import com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage - -class Yuu : YukiHookLoadPackage() { - - override fun onHookStart() { - } -} \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/HookMain.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/HookMain.kt new file mode 100644 index 00000000..8388c982 --- /dev/null +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/HookMain.kt @@ -0,0 +1,55 @@ +/** + * 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.demo.hook + +import android.app.Activity +import android.widget.Toast +import com.highcapable.yukihookapi.hook.factory.encase +import com.highcapable.yukihookapi.hook.proxy.YukiHookInitializeProxy +import com.highcapable.yukihookapi.hook.type.ActivityClass +import com.highcapable.yukihookapi.hook.type.BundleClass + +class HookMain : YukiHookInitializeProxy { + + override fun onHook() = encase { + optApp(name = "com.highcapable.yukihookapi.demo") { + classOf(name = "$packageName.MainActivity").hook { + grabMember = hookClass.methodOf(name = "hello") + replaceTo(any = "这是一段 Hook 的文字内容") + } + } + optApp(name = "com.android.browser") { + ActivityClass.hook { + grabMember = hookClass.methodOf(name = "onCreate", BundleClass) + afterHook { + Toast.makeText(thisAny as Activity, "Hook Success", Toast.LENGTH_SHORT).show() + } + } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index f0335fbf..185268c6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,30 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -plugins { - id 'com.android.application' version '7.1.0' apply false - id 'com.android.library' version '7.1.0' apply false - id 'org.jetbrains.kotlin.android' version '1.6.10' apply false +buildscript { + ext.kotlin_version = "1.6.10" + repositories { + google() + maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url "https://www.jitpack.io" } + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.4" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url "https://www.jitpack.io" } + mavenCentral() + } } task clean(type: Delete) { diff --git a/settings.gradle b/settings.gradle index c5050189..21f6fa4b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,17 +1,3 @@ -pluginManagement { - repositories { - gradlePluginPortal() - google() - mavenCentral() - } -} -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - } -} rootProject.name = "YukiHookAPI" include ':app' include ':yukihookapi' diff --git a/yukihookapi/build.gradle b/yukihookapi/build.gradle index da80ad4e..85629f6d 100644 --- a/yukihookapi/build.gradle +++ b/yukihookapi/build.gradle @@ -31,7 +31,7 @@ android { dependencies { // Used 82 API Version - compileOnly fileTree(include: ['api-82.jar'], dir: 'libs') + compileOnly 'de.robv.android.xposed:api:82' implementation 'androidx.appcompat:appcompat:1.4.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseField.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseField.kt new file mode 100644 index 00000000..77006344 --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseField.kt @@ -0,0 +1,25 @@ +@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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseMethod.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseMethod.kt new file mode 100644 index 00000000..df781cdb --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/DoNotUseMethod.kt @@ -0,0 +1,25 @@ +@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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessField.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessField.kt new file mode 100644 index 00000000..3ce54f96 --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessField.kt @@ -0,0 +1,24 @@ +@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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessMethod.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessMethod.kt new file mode 100644 index 00000000..78e156cb --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/annotation/UseLessMethod.kt @@ -0,0 +1,24 @@ +@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 \ 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 index 2610a8a8..855d8d32 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/YukiHook.kt @@ -25,11 +25,12 @@ * * This file is Created by fankes on 2022/2/2. */ -@file:Suppress("MemberVisibilityCanBePrivate", "unused") +@file:Suppress("MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE") package com.highcapable.yukihookapi.hook import android.content.pm.ApplicationInfo +import com.highcapable.yukihookapi.annotation.DoNotUseField import com.highcapable.yukihookapi.hook.YukiHook.encase import com.highcapable.yukihookapi.param.CustomParam import com.highcapable.yukihookapi.param.PackageParam @@ -42,20 +43,14 @@ import com.highcapable.yukihookapi.param.PackageParam object YukiHook { /** Xposed Hook API 方法体回调 */ + @DoNotUseField internal var packageParamCallback: (PackageParam.() -> Unit)? = null - /** YukiHook 的 API 只能装载一次 */ - private var isLoaded = false - /** - * 自身作为模块装载调用入口方法 - Xposed API - * ⚠️ 注意:只能装载一次 + * 作为模块装载调用入口方法 - Xposed API * @param initiate Hook 方法体 - * @throws IllegalStateException 重复调用会抛出异常 */ fun encase(initiate: PackageParam.() -> Unit) { - if (isLoaded) error("YukiHook API already loaded") - isLoaded = true packageParamCallback = initiate } diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt index 708864c8..8b459a81 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/core/YukiHookCreater.kt @@ -25,39 +25,145 @@ * * This file is Created by fankes on 2022/2/2. */ +@file:Suppress("MemberVisibilityCanBePrivate", "unused") + package com.highcapable.yukihookapi.hook.core +import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.param.HookParam import com.highcapable.yukihookapi.param.PackageParam -import java.lang.reflect.Constructor -import java.lang.reflect.Method +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XC_MethodReplacement +import de.robv.android.xposed.XposedBridge +import java.lang.reflect.Member -class YukiHookCreater(private val param: PackageParam) { +/** + * YukiHook 核心类实现方法 + * 这是一个 API 对接类 - 实现原生对接 [XposedBridge] + * @param instance 需要传入 [PackageParam] 实现方法调用 + * @param hookClass 要 Hook 的 [Class] + */ +class YukiHookCreater(private val instance: PackageParam, val hookClass: Class<*>) { - var grabMethod: Method? = null - var grabConstructor: Constructor<*>? = null + /** @call Base Field */ + private var beforeHookCallback: (HookParam.() -> Unit)? = null + /** @call Base Field */ + private var afterHookCallback: (HookParam.() -> Unit)? = null + + /** @call Base Field */ + private var replaceHookCallback: (HookParam.() -> Any?)? = null + + /** 是否为替换模式 */ + private var isReplaceMode = false + + /** 设置要 Hook 的方法、构造类 */ + var grabMember: Member? = null + + /** + * 在方法执行完成前 Hook + * 不可与 [replaceAny] [replaceVoid] [replaceTo] 同时使用 + * @param initiate [HookParam] 方法体 + */ fun beforeHook(initiate: HookParam.() -> Unit) { - + isReplaceMode = false + beforeHookCallback = initiate } + /** + * 在方法执行完成后 Hook + * 不可与 [replaceAny] [replaceVoid] [replaceTo] 同时使用 + * @param initiate [HookParam] 方法体 + */ fun afterHook(initiate: HookParam.() -> Unit) { - + isReplaceMode = false + afterHookCallback = initiate } - fun replaceHook(initiate: HookParam.() -> Any?) { - + /** + * 替换此方法内容 - 给出返回值 + * 不可与 [beforeHook] [afterHook] 同时使用 + * @param initiate [HookParam] 方法体 + */ + fun replaceAny(initiate: HookParam.() -> Any?) { + isReplaceMode = true + replaceHookCallback = initiate } + /** + * 替换此方法内容 - 没有返回值 (Void) + * 不可与 [beforeHook] [afterHook] 同时使用 + * @param initiate [HookParam] 方法体 + */ + fun replaceVoid(initiate: HookParam.() -> Unit) { + isReplaceMode = true + replaceHookCallback = initiate + } + + /** + * 替换方法返回值 + * 不可与 [beforeHook] [afterHook] 同时使用 + * @param any 要替换为的返回值对象 + */ fun replaceTo(any: Any?) { - + isReplaceMode = true + replaceHookCallback = { any } } + /** + * 替换方法返回值为 true + * 确保替换方法的返回对象为 [Boolean] + * 不可与 [beforeHook] [afterHook] 同时使用 + */ + fun replaceToTrue() { + isReplaceMode = true + replaceHookCallback = { true } + } + + /** + * 替换方法返回值为 false + * 确保替换方法的返回对象为 [Boolean] + * 不可与 [beforeHook] [afterHook] 同时使用 + */ + fun replaceToFalse() { + isReplaceMode = true + replaceHookCallback = { false } + } + + /** + * 拦截此方法 + * 不可与 [beforeHook] [afterHook] 同时使用 + */ fun intercept() { - + isReplaceMode = true + replaceHookCallback = { null } } + /** + * Hook 执行入口 - 不可在外部调用 + * @throws IllegalStateException 如果必要参数没有被设置 + */ + @DoNotUseMethod fun hook() { + if (grabMember == null) error("Target hook method cannot be null") + if (isReplaceMode) + XposedBridge.hookMethod(grabMember, object : XC_MethodReplacement() { + override fun replaceHookedMethod(param: MethodHookParam?): Any? { + if (param == null) return null + return replaceHookCallback?.invoke(HookParam(param)) + } + }) + else + XposedBridge.hookMethod(grabMember, object : XC_MethodHook() { + override fun beforeHookedMethod(param: MethodHookParam?) { + if (param == null) return + beforeHookCallback?.invoke(HookParam(param)) + } + override fun afterHookedMethod(param: MethodHookParam?) { + if (param == null) return + afterHookCallback?.invoke(HookParam(param)) + } + }) } } \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt index e591aa29..77dc0ba2 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt @@ -29,27 +29,12 @@ package com.highcapable.yukihookapi.hook.factory -import android.content.pm.ApplicationInfo import com.highcapable.yukihookapi.hook.YukiHook +import com.highcapable.yukihookapi.hook.proxy.YukiHookInitializeProxy import com.highcapable.yukihookapi.param.PackageParam /** - * Lambda 方式装载 [YukiHook] + * 在 [YukiHookInitializeProxy] 中装载 [YukiHook] * @param initiate Hook 方法体 */ -fun yukiHook(initiate: PackageParam.() -> Unit) = YukiHook.encase(initiate) - -/** - * 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 +fun YukiHookInitializeProxy.encase(initiate: PackageParam.() -> Unit) = YukiHook.encase(initiate) \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookInitializeProxy.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookInitializeProxy.kt new file mode 100644 index 00000000..af94cea4 --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookInitializeProxy.kt @@ -0,0 +1,48 @@ +/** + * 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.hook.YukiHook + +/** + * YukiHook 的 Xposed 装载 API 调用接口 + * 自动调用 [onHook] 完成 Hook 开始操作 + */ +@Keep +interface YukiHookInitializeProxy { + + /** + * 作为模块装载调用入口方法 - Xposed API + * 请在此方法中调用 [YukiHook.encase] + */ + @Keep + fun onHook() +} \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ComponentTypeFactory.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ComponentTypeFactory.kt new file mode 100644 index 00000000..11a237b9 --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ComponentTypeFactory.kt @@ -0,0 +1,80 @@ +/** + * 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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt new file mode 100644 index 00000000..7f8e8a6f --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/VariableTypeFactory.kt @@ -0,0 +1,98 @@ +/** + * 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 + +/** + * 获得 [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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ViewTypeFactory.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ViewTypeFactory.kt new file mode 100644 index 00000000..e3a279dc --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/type/ViewTypeFactory.kt @@ -0,0 +1,82 @@ +/** + * 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 \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt index 7ef8af9f..7ac030e4 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/YukiHookLoadPackage.kt @@ -25,8 +25,11 @@ * * This file is Created by fankes on 2022/2/2. */ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package com.highcapable.yukihookapi.hook.xposed +import android.util.Log import androidx.annotation.Keep import com.highcapable.yukihookapi.hook.YukiHook import com.highcapable.yukihookapi.param.PackageParam @@ -35,14 +38,30 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage /** * 接管 Xposed 的 [IXposedHookLoadPackage] 入口 - * 你可以使用 [YukiHook] 来监听模块开始装载 + * 你可以使用 [YukiHook.encase] 来监听模块开始装载 */ @Keep class YukiHookLoadPackage : IXposedHookLoadPackage { override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { if (lpparam == null) return + try { + /** 执行入口方法 */ + Class.forName(hookEntryClassName()).apply { + getDeclaredMethod("onHook").apply { isAccessible = true } + .invoke(getDeclaredConstructor().apply { isAccessible = true }.newInstance()) + } + } catch (e: Throwable) { + Log.e("YukiHookAPI", "Try to load ${hookEntryClassName()} Failed", e) + } /** 设置装载回调 */ YukiHook.packageParamCallback?.invoke(PackageParam(lpparam)) } + + /** + * 获得目标装载类名 - AOP + * @return [String] 目标装载类名 + */ + @Keep + private fun hookEntryClassName() = "com.highcapable.yukihookapi.demo.hook.HookMain" } \ 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 6001e00f..0e778456 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt @@ -25,13 +25,15 @@ * * This file is Created by fankes on 2022/2/2. */ -@file:Suppress("unused", "MemberVisibilityCanBePrivate") +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_API_USAGE") 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 +import java.lang.reflect.Constructor +import java.lang.reflect.Method /** * 装载 Hook 的目标 APP 入口对象实现类 @@ -95,10 +97,29 @@ class PackageParam( */ fun classOf(name: String): Class<*> = appClassLoader.loadClass(name) + /** + * 查找目标方法 + * @param name 方法名 + * @param params 方法参数 - 没有可空 + * @return [Method] + * @throws NoSuchMethodError 如果找不到方法会报错 + */ + fun Class<*>.methodOf(name: String, vararg params: Class<*>): Method = + getDeclaredMethod(name, *params).apply { isAccessible = true } + + /** + * 查找目标构造类 + * @param params 方法参数 - 没有可空 + * @return [Constructor] + * @throws NoSuchMethodError 如果找不到方法会报错 + */ + fun Class<*>.constructorOf(vararg params: Class<*>): Constructor<*> = + getDeclaredConstructor(*params).apply { isAccessible = true } + /** * Hook 方法、构造类 * @param initiate 方法体 */ fun Class<*>.hook(initiate: YukiHookCreater.() -> Unit) = - YukiHookCreater(param = this@PackageParam).apply(initiate).hook() + YukiHookCreater(instance = this@PackageParam, hookClass = this).apply(initiate).hook() } \ No newline at end of file