This commit is contained in:
2022-02-02 05:44:17 +08:00
parent 251b2053bd
commit 9c0ec20b90
20 changed files with 704 additions and 92 deletions

40
.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://www.jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://maven.aliyun.com/nexus/content/repositories/jcenter" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

View File

@@ -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() = "正常显示的一行文字"
}

View File

@@ -1,9 +0,0 @@
package com.highcapable.yukihookapi.demo
import com.highcapable.yukihookapi.hook.xposed.YukiHookLoadPackage
class Yuu : YukiHookLoadPackage() {
override fun onHookStart() {
}
}

View File

@@ -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()
}
}
}
}
}

View File

@@ -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) {

View File

@@ -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'

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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))
}
})
}
}

View File

@@ -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)
fun YukiHookInitializeProxy.encase(initiate: PackageParam.() -> Unit) = YukiHook.encase(initiate)

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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()
}