From d3ab6d58b89f6385e27a97609cc11900ae42c6b6 Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Fri, 4 Feb 2022 00:36:51 +0800 Subject: [PATCH] ... --- .../yukihookapi/demo/hook/MainHooker.kt | 17 +-- .../yukihookapi/demo/hook/SecondHooker.kt | 33 ++++- .../{ThirdHooker.kt => TestChildHooker.kt} | 33 +---- .../demo/hook/inject/MainInjecter.kt | 114 ++++++++++++++++++ .../highcapable/yukihookapi/YukiHookAPI.kt | 16 +++ .../yukihookapi/hook/entity/YukiBaseHooker.kt | 9 ++ .../hook/factory/YukiHookFactory.kt | 10 ++ .../hook/proxy/YukiHookXposedInitProxy.kt | 8 ++ .../yukihookapi/param/PackageParam.kt | 26 ++-- 9 files changed, 210 insertions(+), 56 deletions(-) rename app/src/main/java/com/highcapable/yukihookapi/demo/hook/{ThirdHooker.kt => TestChildHooker.kt} (54%) create mode 100644 app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/MainHooker.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/MainHooker.kt index b044ba7b..0e32f27f 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/MainHooker.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/MainHooker.kt @@ -29,21 +29,16 @@ package com.highcapable.yukihookapi.demo.hook -import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.demo.BuildConfig import com.highcapable.yukihookapi.demo.InjectTest import com.highcapable.yukihookapi.demo.MainActivity -import com.highcapable.yukihookapi.hook.factory.encase -import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy +import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.type.StringType -@InjectYukiHookWithXposed -class MainHooker : YukiHookXposedInitProxy { +class MainHooker : YukiBaseHooker() { - private val moduleName = BuildConfig.APPLICATION_ID - - override fun onHook() = encase(moduleName) { - loadApp(name = moduleName) { + override fun onHook() = + loadApp(name = BuildConfig.APPLICATION_ID) { MainActivity::class.java.hook { injectMember { method { @@ -67,8 +62,6 @@ class MainHooker : YukiHookXposedInitProxy { beforeHook { args().set("构造方法已被 Hook 成功") } } } - loadHooker(hooker = SecondHooker()) + loadHooker(hooker = TestChildHooker()) } - loadApp(name = "com.android.browser", hooker = ThirdHooker()) - } } \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/SecondHooker.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/SecondHooker.kt index fa9868da..cd0a8fe6 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/SecondHooker.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/SecondHooker.kt @@ -27,17 +27,40 @@ */ package com.highcapable.yukihookapi.demo.hook +import android.app.AlertDialog +import android.widget.Toast import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker -import com.highcapable.yukihookapi.hook.type.StringType +import com.highcapable.yukihookapi.hook.factory.findMethod +import com.highcapable.yukihookapi.hook.type.ActivityClass +import com.highcapable.yukihookapi.hook.type.BundleClass // for test class SecondHooker : YukiBaseHooker() { override fun onHook() = - findClass(name = "$packageName.InjectTestName").hook { - injectMember { - constructor { param(StringType) } - beforeHook { args().set("构造方法已被 Hook 成功 [2]") } + loadApp(name = "com.android.browser") { + ActivityClass.hook { + injectMember { + method { + name = "onCreate" + param(BundleClass) + } + afterHook { + AlertDialog.Builder(instance()) + .setCancelable(false) + .setTitle("测试 Hook") + .setMessage("Hook 已成功") + .setPositiveButton("OK") { _, _ -> + Toast.makeText(instance(), "Hook Success", Toast.LENGTH_SHORT).show() + }.show() + } + } + injectMember { + member = hookClass.findMethod(name = "onStart") + afterHook { + Toast.makeText(instance(), "手动 Hook", Toast.LENGTH_SHORT).show() + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/ThirdHooker.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/TestChildHooker.kt similarity index 54% rename from app/src/main/java/com/highcapable/yukihookapi/demo/hook/ThirdHooker.kt rename to app/src/main/java/com/highcapable/yukihookapi/demo/hook/TestChildHooker.kt index 370a4469..7935e93b 100644 --- a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/ThirdHooker.kt +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/TestChildHooker.kt @@ -23,42 +23,21 @@ * 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. + * This file is Created by fankes on 2022/2/4. */ package com.highcapable.yukihookapi.demo.hook -import android.app.AlertDialog -import android.widget.Toast import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker -import com.highcapable.yukihookapi.hook.factory.findMethod -import com.highcapable.yukihookapi.hook.type.ActivityClass -import com.highcapable.yukihookapi.hook.type.BundleClass +import com.highcapable.yukihookapi.hook.type.StringType // for test -class ThirdHooker : YukiBaseHooker() { +class TestChildHooker : YukiBaseHooker() { override fun onHook() = - ActivityClass.hook { + findClass(name = "$packageName.InjectTestName").hook { injectMember { - method { - name = "onCreate" - param(BundleClass) - } - afterHook { - AlertDialog.Builder(instance()) - .setCancelable(false) - .setTitle("测试 Hook") - .setMessage("Hook 已成功") - .setPositiveButton("OK") { _, _ -> - Toast.makeText(instance(), "Hook Success", Toast.LENGTH_SHORT).show() - }.show() - } - } - injectMember { - member = hookClass.findMethod(name = "onStart") - afterHook { - Toast.makeText(instance(), "手动 Hook", Toast.LENGTH_SHORT).show() - } + constructor { param(StringType) } + beforeHook { args().set("构造方法已被 Hook 成功 [2]") } } } } \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt new file mode 100644 index 00000000..385c196c --- /dev/null +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/hook/inject/MainInjecter.kt @@ -0,0 +1,114 @@ +/** + * 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.demo.hook.inject + +import android.app.AlertDialog +import android.widget.Toast +import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed +import com.highcapable.yukihookapi.demo.BuildConfig +import com.highcapable.yukihookapi.demo.InjectTest +import com.highcapable.yukihookapi.demo.MainActivity +import com.highcapable.yukihookapi.demo.hook.MainHooker +import com.highcapable.yukihookapi.demo.hook.SecondHooker +import com.highcapable.yukihookapi.hook.factory.encase +import com.highcapable.yukihookapi.hook.factory.findMethod +import com.highcapable.yukihookapi.hook.proxy.YukiHookXposedInitProxy +import com.highcapable.yukihookapi.hook.type.ActivityClass +import com.highcapable.yukihookapi.hook.type.BundleClass +import com.highcapable.yukihookapi.hook.type.StringType + +// for test +@InjectYukiHookWithXposed +class MainInjecter : YukiHookXposedInitProxy { + + override fun onHook() { + // 方案 1 + encase(BuildConfig.APPLICATION_ID, MainHooker(), SecondHooker()) + // 方案 2 + encase(BuildConfig.APPLICATION_ID) { + loadApp(name = BuildConfig.APPLICATION_ID) { + MainActivity::class.java.hook { + injectMember { + method { + name = "test" + returnType = StringType + } + replaceTo("这段文字已被 Hook 成功") + } + injectMember { + method { + name = "test" + param(StringType) + returnType = StringType + } + beforeHook { args().set("方法参数已被 Hook 成功") } + } + } + InjectTest::class.java.hook { + injectMember { + constructor { param(StringType) } + beforeHook { args().set("构造方法已被 Hook 成功") } + } + } + findClass(name = "$packageName.InjectTestName").hook { + injectMember { + constructor { param(StringType) } + beforeHook { args().set("构造方法已被 Hook 成功 [2]") } + } + } + } + loadApp(name = "com.android.browser") { + ActivityClass.hook { + injectMember { + method { + name = "onCreate" + param(BundleClass) + } + afterHook { + AlertDialog.Builder(instance()) + .setCancelable(false) + .setTitle("测试 Hook") + .setMessage("Hook 已成功") + .setPositiveButton("OK") { _, _ -> + Toast.makeText(instance(), "Hook Success", Toast.LENGTH_SHORT).show() + }.show() + } + } + injectMember { + member = hookClass.findMethod(name = "onStart") + afterHook { + Toast.makeText(instance(), "手动 Hook", Toast.LENGTH_SHORT).show() + } + } + } + } + } + } +} \ No newline at end of file diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/YukiHookAPI.kt index 5fcd0272..337379b0 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -32,6 +32,7 @@ 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 @@ -65,6 +66,21 @@ object YukiHookAPI { 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] diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt index a3d34e5f..60aacd81 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/entity/YukiBaseHooker.kt @@ -65,6 +65,15 @@ import com.highcapable.yukihookapi.param.PackageParam */ abstract class YukiBaseHooker : PackageParam() { + /** + * 赋值并克隆一个 [PackageParam] + * @param packageParam 需要使用的 [PackageParam] + */ + internal fun assignInstance(packageParam: PackageParam) { + baseAssignInstance(packageParam) + onHook() + } + /** 子类 Hook 开始 */ abstract fun onHook() } \ 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 fbd42c22..9f8d4aa6 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 @@ -34,6 +34,7 @@ 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 @@ -45,6 +46,15 @@ import com.highcapable.yukihookapi.param.PackageParam 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] 是否激活 diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookXposedInitProxy.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookXposedInitProxy.kt index b7906d16..aab5c1b8 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookXposedInitProxy.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/proxy/YukiHookXposedInitProxy.kt @@ -64,6 +64,14 @@ import com.highcapable.yukihookapi.hook.factory.encase * * .... * + * 若你喜欢分类创建 Hooker - 还可以这样写: + * + * ...... + * + * override fun onHook() = encase(moduleName = "模块包名", MainHooker(), SecondHooker(), ThirdHooker() ...) + * + * ...... + * * 详情请参考 https://github.com/fankes/YukiHookAPI/wiki */ @Keep 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 bdababdd..f4f2a419 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/param/PackageParam.kt @@ -44,8 +44,8 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage * @param customParam 自定义装载类 - 默认空 */ open class PackageParam( - private val baseParam: XC_LoadPackage.LoadPackageParam? = null, - private val customParam: CustomParam? = null + private var baseParam: XC_LoadPackage.LoadPackageParam? = null, + private var customParam: CustomParam? = null ) { /** @@ -85,6 +85,15 @@ open class PackageParam( */ val isFirstApplication get() = baseParam?.isFirstApplication ?: true + /** + * 赋值并克隆另一个 [PackageParam] + * @param another 另一个 [PackageParam] + */ + internal fun baseAssignInstance(another: PackageParam) { + this.baseParam = another.baseParam + this.customParam = another.customParam + } + /** * 装载并 Hook 指定包名的 APP * @param name 包名 @@ -94,20 +103,13 @@ open class PackageParam( if (packageName == name) initiate(this) } - /** - * 装载并 Hook 指定包名的 APP - * @param name 包名 - * @param hooker Hook 子类 - */ - fun loadApp(name: String, hooker: YukiBaseHooker) { - if (packageName == name) hooker.onHook() - } - /** * 装载 Hook 子类 + * + * 你可以在 Hooker 中继续装载 Hooker * @param hooker Hook 子类 */ - fun loadHooker(hooker: YukiBaseHooker) = hooker.onHook() + fun loadHooker(hooker: YukiBaseHooker) = hooker.assignInstance(packageParam = this) /** * 将目标 [Class] 绑定到 [appClassLoader]