diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..02f50092 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,24 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-dontwarn +-ignorewarnings + +-optimizationpasses 10 +-dontusemixedcaseclassnames + +-dontoptimize +-verbose +-overloadaggressively +-repackageclasses i +-allowaccessmodification + +-adaptclassstrings +-adaptresourcefilenames +-adaptresourcefilecontents + +# -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-renamesourcefileattribute P +-keepattributes SourceFile,LineNumberTable \ No newline at end of file diff --git a/app/src/main/java/com/highcapable/yukihookapi/demo/App.kt b/app/src/main/java/com/highcapable/yukihookapi/demo/App.kt new file mode 100644 index 00000000..c5433cbd --- /dev/null +++ b/app/src/main/java/com/highcapable/yukihookapi/demo/App.kt @@ -0,0 +1,52 @@ +/** + * 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/7. + */ +@file:Suppress("unused") + +package com.highcapable.yukihookapi.demo + +import android.app.Application +import android.content.Context +import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.demo.hook.MainHooker +import com.highcapable.yukihookapi.demo.hook.SecondHooker + +class App : Application() { + + override fun attachBaseContext(base: Context?) { + //// + // 装载你的 Hook 框架的代码 + //// + // 方式 1 + YukiHookAPI.encase(base) { + // Your code here. + } + // 方式 2 + YukiHookAPI.encase(base, MainHooker(), SecondHooker()) + super.attachBaseContext(base) + } +} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index 49dee0da..4a7b1c64 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -29,12 +29,14 @@ package com.highcapable.yukihookapi -import android.content.pm.ApplicationInfo +import android.app.Application +import android.content.Context import com.highcapable.yukihookapi.YukiHookAPI.configs import com.highcapable.yukihookapi.YukiHookAPI.encase import com.highcapable.yukihookapi.annotation.DoNotUseMethod import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker -import com.highcapable.yukihookapi.hook.param.CustomParam +import com.highcapable.yukihookapi.hook.factory.processName +import com.highcapable.yukihookapi.hook.param.EnvironmentParam import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus import de.robv.android.xposed.callbacks.XC_LoadPackage @@ -54,7 +56,7 @@ object YukiHookAPI { private var packageParamCallback: (PackageParam.() -> Unit)? = null /** - * 配置 YukiHook + * 配置 YukiHookAPI */ object Configs { @@ -102,7 +104,17 @@ object YukiHookAPI { * @param lpparam Xposed [XC_LoadPackage.LoadPackageParam] */ @DoNotUseMethod - fun onXposedLoaded(lpparam: XC_LoadPackage.LoadPackageParam) = packageParamCallback?.invoke(PackageParam(lpparam)) + fun onXposedLoaded(lpparam: XC_LoadPackage.LoadPackageParam) = + packageParamCallback?.invoke( + PackageParam( + EnvironmentParam( + packageName = lpparam.packageName, + processName = lpparam.processName, + appClassLoader = lpparam.classLoader, + appInfo = lpparam.appInfo + ) + ) + ) /** * 作为模块装载调用入口方法 - Xposed API @@ -126,16 +138,87 @@ object YukiHookAPI { } /** - * 自定义 Hook 方法装载入口 - * @param classLoader [ClassLoader] - * @param packageName 包名 - * @param appInfo [ApplicationInfo] + * 作为 [Application] 装载调用入口方法 + * + * 请在 [Application.attachBaseContext] 中写入如下代码: + * + * .... + * + * override fun attachBaseContext(base: Context?) { + * + * ....//// + * + * ....// 装载你使用的 Hook 框架的代码 + * + * ....// 你的 Hook 框架需要支持 Xposed API + * + * ....//// + * + * ....// 装载 YukiHookAPI + * + * ....YukiHookAPI.encase(base) { + * + * ........// Your code here. + * + * ....} + * + * ....super.attachBaseContext(base) + * + * } + * + * .... + * + * 详情请参考 [YukiHookAPI Wiki](https://github.com/fankes/YukiHookAPI/wiki) + * @param baseContext attachBaseContext * @param initiate Hook 方法体 */ - fun encase( - classLoader: ClassLoader, - packageName: String, - appInfo: ApplicationInfo, - initiate: PackageParam.() -> Unit - ) = initiate.invoke(PackageParam(customParam = CustomParam(classLoader, appInfo, packageName))) + fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit) { + if (baseContext != null) initiate.invoke(baseContext.packagePararm) + } + + /** + * 作为 [Application] 装载调用入口方法 + * + * 请在 [Application.attachBaseContext] 中写入如下代码: + * + * .... + * + * override fun attachBaseContext(base: Context?) { + * + * ....//// + * + * ....// 装载你使用的 Hook 框架的代码 + * + * ....// 你的 Hook 框架需要支持 Xposed API + * + * ....//// + * + * ....// 装载 YukiHookAPI + * + * ....YukiHookAPI.encase(base, MainHooker(), SecondHooker() ...) + * + * ....super.attachBaseContext(base) + * + * } + * + * .... + * + * 详情请参考 [YukiHookAPI Wiki](https://github.com/fankes/YukiHookAPI/wiki) + * @param baseContext attachBaseContext + * @param hooker Hook 子类数组 - 必填不能为空 + * @throws IllegalStateException 如果 [hooker] 是空的 + */ + fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker) { + if (baseContext != null) + if (hooker.isNotEmpty()) + hooker.forEach { it.assignInstance(packageParam = baseContext.packagePararm) } + else error("Hooker is empty") + } + + /** + * 通过 baseContext 创建 Hook 入口类 + * @return [PackageParam] + */ + private val Context.packagePararm + get() = PackageParam(EnvironmentParam(packageName, processName, classLoader, applicationInfo)) } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt index 50482079..ae27d6ac 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/ConstructorFinder.kt @@ -46,17 +46,18 @@ import java.lang.reflect.Constructor */ class ConstructorFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { - /** 失败尝试次数数组 */ - private val remedyPlan = HashMap() - - /** 构造方法参数 */ + /** [Constructor] 参数数组 */ private var params: Array>? = null /** - * 构造方法参数 + * [Constructor] 参数 + * - 无参 [Constructor] 不要使用此方法 + * + * - 有参 [Constructor] 必须使用此方法设定参数 * @param param 参数数组 */ fun param(vararg param: Class<*>) { + if (param.isEmpty()) error("param is empty, please delete param() method") params = param } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt index b24d95ff..7d86ab76 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/FieldFinder.kt @@ -48,10 +48,16 @@ class FieldFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, p /** 当前找到的 [Field] */ private var fieldInstance: Field? = null - /** 变量名 */ + /** + * [Field] 名称 + * - 必须设置 + */ var name = "" - /** 变量类型 */ + /** + * [Field] 类型 + * - 必须设置 + */ var type: Class<*>? = null /** diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt index 670a883a..7e3104c0 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/finder/MethodFinder.kt @@ -46,20 +46,32 @@ import java.lang.reflect.Method */ class MethodFinder(private val hookInstance: YukiHookCreater.MemberHookCreater, private val hookClass: Class<*>) { - /** 方法参数 */ + /** [Method] 参数数组 */ private var params: Array>? = null - /** 方法名 */ + /** + * [Method] 名称 + * + * - 必须设置 + */ var name = "" - /** 方法返回值 */ + /** + * [Method] 返回值 + * + * 可不填写返回值 - 默认模糊查找并取第一个匹配的 [Method] + */ var returnType: Class<*>? = null /** - * 方法参数 + * [Method] 参数 + * - 无参 [Method] 不要使用此方法 + * + * - 有参 [Method] 必须使用此方法设定参数 * @param param 参数数组 */ fun param(vararg param: Class<*>) { + if (param.isEmpty()) error("param is empty, please delete param() method") params = param } diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt index 9b4e41c6..164ca26b 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/factory/YukiHookFactory.kt @@ -33,10 +33,14 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle +import android.os.Process import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy +import java.io.BufferedReader +import java.io.File +import java.io.FileReader /** * 在 [YukiHookXposedInitProxy] 中装载 [YukiHookAPI] @@ -51,6 +55,22 @@ fun YukiHookXposedInitProxy.encase(initiate: PackageParam.() -> Unit) = YukiHook */ fun YukiHookXposedInitProxy.encase(vararg hooker: YukiBaseHooker) = YukiHookAPI.encase(hooker = hooker) +/** + * 获取当前进程名称 + * @return [String] + */ +val Context.processName + get() = try { + BufferedReader(FileReader(File("/proc/${Process.myPid()}/cmdline"))).let { buff -> + buff.readLine().trim { it <= ' ' }.let { + buff.close() + it + } + } + } catch (_: Throwable) { + packageName ?: "" + } + /** * 判断模块是否在太极、无极中激活 * @return [Boolean] 是否激活 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/CustomParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/EnvironmentParam.kt similarity index 81% rename from yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/CustomParam.kt rename to yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/EnvironmentParam.kt index ca3e7a0f..05dd4e35 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/CustomParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/EnvironmentParam.kt @@ -23,20 +23,24 @@ * 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. + * This file is Created by fankes on 2022/2/7. */ +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_API_USAGE") + package com.highcapable.yukihookapi.hook.param import android.content.pm.ApplicationInfo /** - * 自定义 [PackageParam] 的装载入口置换类 + * Hook 环境装载实现类 + * @param packageName 包名 + * @param processName 当前进程名 * @param appClassLoader APP [ClassLoader] * @param appInfo APP [ApplicationInfo] - * @param packageName 包名 */ -class CustomParam( +class EnvironmentParam( + var packageName: String, + var processName: String, var appClassLoader: ClassLoader, - var appInfo: ApplicationInfo, - var packageName: String + var appInfo: ApplicationInfo ) \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt index d00cca4a..e9fe8f67 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/param/PackageParam.kt @@ -33,21 +33,12 @@ 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 自定义装载类 - 默认空 + * @param baseParam 对接环境装载类的实现 - 默认是空的 */ -open class PackageParam( - private var baseParam: XC_LoadPackage.LoadPackageParam? = null, - private var customParam: CustomParam? = null -) { +open class PackageParam(private var baseParam: EnvironmentParam? = null) { /** * 获取当前 APP 的 [ClassLoader] @@ -55,36 +46,33 @@ open class PackageParam( * @throws IllegalStateException 如果 [ClassLoader] 是空的 */ val appClassLoader - get() = baseParam?.classLoader ?: customParam?.appClassLoader ?: javaClass.classLoader - ?: error("PackageParam ClassLoader is null") + get() = baseParam?.appClassLoader ?: javaClass.classLoader ?: error("PackageParam got null ClassLoader") /** * 获取当前 APP 的 [ApplicationInfo] * @return [ApplicationInfo] */ - val appInfo get() = baseParam?.appInfo ?: customParam?.appInfo ?: ApplicationInfo() + val appInfo get() = baseParam?.appInfo ?: ApplicationInfo() /** * 获取当前 APP 的进程名称 * - * 默认的进程名称是 [packageName] 如果自定义了 [customParam] 将返回包名 + * 默认的进程名称是 [packageName] * @return [String] */ - val processName get() = baseParam?.processName ?: customParam?.packageName ?: "" + val processName get() = baseParam?.processName ?: packageName /** * 获取当前 APP 的包名 * @return [String] */ - val packageName get() = baseParam?.packageName ?: customParam?.packageName ?: "" + val packageName get() = baseParam?.packageName ?: "" /** * 获取当前 APP 是否为第一个 Application - * - * 若自定义了 [customParam] 将永远返回 true * @return [Boolean] */ - val isFirstApplication get() = baseParam?.isFirstApplication ?: true + val isFirstApplication get() = packageName == processName /** * 赋值并克隆另一个 [PackageParam] @@ -94,7 +82,6 @@ open class PackageParam( @DoNotUseMethod internal fun baseAssignInstance(another: PackageParam) { this.baseParam = another.baseParam - this.customParam = another.customParam } /**