Added DexClassFinder and toClass, hasClass function using in reflection documentation

This commit is contained in:
2022-09-21 14:31:27 +08:00
parent 41546b2c9f
commit 20663f54d8

View File

@@ -8,15 +8,359 @@
### 对象转换 ### 对象转换
> 敬请期待 假设我们要得到一个不能直接调用的 `Class`,通常情况下,我们可以使用标准的反射 API 去查找这个 `Class`
> 示例如下
```kotlin
// 默认 ClassLoader 环境下的 Class
var instance = Class.forName("com.demo.Test")
// 指定 ClassLoader 环境下的 Class
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
var instance = customClassLoader?.loadClass("com.demo.Test")
```
这种写法大概不是很友好,此时 `YukiHookAPI` 就为你提供了一个可在任意地方使用的语法糖。
以上写法换做 `YukiHookAPI` 可写作如下形式。
> 示例如下
```kotlin
// 直接得到这个 Class
// 如果当前正处于 PackageParam 环境,那么你可以不需要考虑 ClassLoader
var instance = "com.demo.Test".toClass()
// 自定义 Class 所在的 ClassLoader
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
var instance = "com.demo.Test".toClass(customClassLoader)
```
我们还可以通过映射来得到一个存在的 `Class` 对象。
> 示例如下
```kotlin
// 假设这个 Class 是能够被直接得到的
var instance = classOf<Test>()
// 我们同样可以自定义 Class 所在的 ClassLoader这对于 stub 来说非常有效
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
var instance = classOf<Test>(customClassLoader)
```
::: tip
更多功能请参考 [String.toClass](../public/com/highcapable/yukihookapi/hook/factory/ReflectionFactory#string-toclass-ext-method)、[PackageParam → String+VariousClass.toClass](../public/com/highcapable/yukihookapi/hook/param/PackageParam#string-variousclass-toclass-i-ext-method)、[classOf](../public/com/highcapable/yukihookapi/hook/factory/ReflectionFactory#classof-method) 方法。
:::
### 存在判断 ### 存在判断
> 敬请期待 假设我们要判断一个 `Class` 是否存在,通常情况下,我们可以使用标准的反射 API 去查找这个 `Class` 通过异常来判断是否存在
### 模糊查询 > 示例如下
> 敬请期待。 ```kotlin
// 默认 ClassLoader 环境下的 Class
var isExist = try {
Class.forName("com.demo.Test")
true
} catch (_: Throwable) {
false
}
// 指定 ClassLoader 环境下的 Class
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
var isExist = try {
customClassLoader?.loadClass("com.demo.Test")
true
} catch (_: Throwable) {
false
}
```
这种写法大概不是很友好,此时 `YukiHookAPI` 就为你提供了一个可在任意地方使用的语法糖。
以上写法换做 `YukiHookAPI` 可写作如下形式。
> 示例如下
```kotlin
// 判断这个 Class 是否存在
// 如果当前正处于 PackageParam 环境,那么你可以不需要考虑 ClassLoader
var isExist = "com.demo.Test".hasClass()
// 自定义 Class 所在的 ClassLoader
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
var isExist = "com.demo.Test".hasClass(customClassLoader)
```
::: tip
更多功能请参考 [String.hasClass](../public/com/highcapable/yukihookapi/hook/factory/ReflectionFactory#string-hasclass-ext-method)、[PackageParam → String.hasClass](../public/com/highcapable/yukihookapi/hook/param/PackageParam#string-hasclass-i-ext-method) 方法。
:::
### 模糊查找&ensp;<Badge type="tip" text="Beta" vertical="middle" />
在 R8 等工具混淆后的宿主 **Dex** 中的 `Class` 名称将会难以分辨,且不确定其正确位置,不能直接通过 [对象转换](#对象转换) 来得到。
此时就有了 `DexClassFinder`,它的作用是通过需要查找的 `Class` 中的字节码特征来确定这个 `Class` 的实例。
::: warning
目前 **DexClassFinder** 的功能尚在试验阶段,由于仅通过 Java 层实现查找功能,在宿主 **Class** 过多时性能可能不能达到最佳水平,如果发生查找不到、定位有误的问题欢迎向我们反馈。
由于是反射层面的 API目前它只能通过**类与成员**的特征来定位指定的 **Class**,不能通过指定字节码中的字符串和方法内容特征来进行定位。
查找 **Class** 的速度取决于当前设备的性能,目前主流的移动端处理器在 **10~15w** 数量的 **Class** 中条件不算复杂的情况下大概在 **3~10s** 区间,条件稍微复杂的情况下最快速度能达到 **25s** 以内,匹配到的同类型 **Class** 越多速度越慢。
:::
#### 开始使用
下面是一个简单的用法示例。
假设下面这个 `Class` 是我们想要得到的,其中的名称经过了混淆,在每个版本可能都不一样。
> 示例如下
```java:no-line-numbers
package com.demo;
public class a extends Activity implements Serializable {
public a(String var1) {
// ...
}
private String a;
private String b;
private boolean a;
protected void onCreate(Bundle var1) {
// ...
}
private static void a(String var1) {
// ...
}
private String a(boolean var1, String var2) {
// ...
}
private void a() {
// ...
}
public void a(boolean var1, a var2, b var3, String var4) {
// ...
}
}
```
此时,我们想得到这个 `Class`,可以直接使用 `ClassLoader.searchClass` 方法。
在 `PackageParam` 中,你可以直接使用 `searchClass` 方法,它将自动指定 `appClassLoader`。
下方演示的条件中每一个都是可选的,条件越复杂定位越精确,同时性能也会越差。
> 示例如下
```kotlin
searchClass {
// 从指定的包名范围搜索,实际使用时,你可以同时指定多个包名范围
from("com.demo")
// 指定当前 Class 的 getSimpleName 的结果,你可以直接对这个字符串进行逻辑判断
// 这里我们不确定它的名称是不是 a可以只判断字符串长度
simpleName { it.length == 1 }
// 指定继承的父类对象,如果是存在的 stub可以直接用泛型表示
extends<Activity>()
// 指定继承的父类对象,可以直接写为完整类名,你还可以同时指定多个
extends("android.app.Activity")
// 指定实现的接口,如果是存在的 stub可以直接用泛型表示
implements<Serializable>()
// 指定实现的接口,可以直接写为完整类名,你还可以同时指定多个
implements("java.io.Serializable")
// 指定构造方法的类型与样式,以及在当前类中存在的个数 count
constructor { param(StringType) }.count(num = 1)
// 指定变量的类型与样式,以及在当前类中存在的个数 count
field { type = StringType }.count(num = 2)
// 指定变量的类型与样式,以及在当前类中存在的个数 count
field { type = BooleanType }.count(num = 1)
// 直接指定所有变量在当前类中存在的个数 count
field().count(num = 3)
// 如果你认为变量的个数是不确定的,还可以使用如下自定义条件
field().count(1..3)
field().count { it >= 3 }
// 指定方法的类型与样式,以及在当前类中存在的个数 count
method {
name = "onCreate"
param(BundleClass)
}.count(num = 1)
// 指定方法的类型与样式,同时指定修饰符,以及在当前类中存在的个数 count
method {
modifiers { isStatic && isPrivate }
param(StringType)
returnType = UnitType
}.count(num = 1)
// 指定方法的类型与样式,同时指定修饰符,以及在当前类中存在的个数 count
method {
modifiers { isPrivate && isStatic.not() }
param(BooleanType, StringType)
returnType = StringType
}.count(num = 1)
// 指定方法的类型与样式,同时指定修饰符,以及在当前类中存在的个数 count
method {
modifiers { isPrivate && isStatic.not() }
emptyParam()
returnType = UnitType
}.count(num = 1)
// 指定方法的类型与样式,同时指定修饰符和模糊类型 VagueType以及在当前类中存在的个数 count
method {
modifiers { isPrivate && isStatic.not() }
param(BooleanType, VagueType, VagueType, StringType)
returnType = UnitType
}.count(num = 1)
// 直接指定所有方法在当前类中存在的个数 count
method().count(num = 5)
// 如果你认为方法的个数是不确定的,还可以使用如下自定义条件
method().count(1..5)
method().count { it >= 5 }
// 直接指定所有成员 (Member) 在当前类中存在的个数 count
// 成员包括Field (变量)、Method (方法)、Constructor (构造方法)
member().count(num = 9)
// 所有成员中一定存在一个 static 修饰符,可以这样加入此条件
member {
modifiers { isStatic }
}
}.get() // 得到这个 Class 本身的实例,找不到会返回 null
```
::: tip
上述用法中对于 **Field**、**Method**、**Constructor** 的条件用法与 [Member 扩展](#member-扩展) 中的相关用法是一致的,仅有小部分区别。
更多功能请参考 [MemberRules](../public/com/highcapable/yukihookapi/hook/core/finder/classes/rules/MemberRules)、[FieldRules](../public/com/highcapable/yukihookapi/hook/core/finder/classes/rules/FieldRules)、[MethodRules](../public/com/highcapable/yukihookapi/hook/core/finder/classes/rules/MethodRules)、[ConstructorRules](../public/com/highcapable/yukihookapi/hook/core/finder/classes/rules/ConstructorRules)。
:::
#### 异步查找
默认情况下 `DexClassFinder` 会使用同步方式查找 `Class`,会阻塞当前线程直到找到或找不到发生异常为止,若查找消耗的时间过长,可能会导致宿主发生 **ANR** 问题。
针对上述问题,我们可以启用异步,只需要加入参数 `async = true`这将不需要你再次启动一个线程API 已帮你处理好相关问题。
::: warning
对于异步情况下你需要使用 **wait** 方法来得到结果,**get** 方法将不再起作用。
:::
> 示例如下
```kotlin
searchClass(async = true) {
// ...
}.wait { class1 ->
// 得到异步结果
}
searchClass(async = true) {
// ...
}.wait { class2 ->
// 得到异步结果
}
```
这样我们的查找过程就是异步运行了,它将不会阻塞主线程,每个查找都将在单独的线程同时进行,可达到并行任务的效果。
#### 本地缓存
由于每次重新打开宿主都会重新进行查找,在宿主版本不变的情况下这是一种重复性能浪费。
此时我们可以通过指定 `name` 参数来对当前宿主版本的查找结果进行本地缓存,下一次将直接从本地缓存中读取查找到的类名。
本地缓存使用的是 `SharedPreferences`,它将被保存到宿主的数据目录中,在宿主版本更新后会重新进行缓存。
启用本地缓存后,将同时设置 `async = true`,你可以不需要再手动进行设置。
> 示例如下
```kotlin
searchClass(name = "com.demo.class1") {
// ...
}.wait { class1 ->
// 得到异步结果
}
searchClass(name = "com.demo.class2") {
// ...
}.wait { class2 ->
// 得到异步结果
}
```
如果你想手动清除本地缓存,可以使用如下方法清除当前版本的宿主缓存。
> 示例如下
```kotlin
// 直接调用,在宿主的 appContext 为空时可能会失败,失败会打印警告信息
DexClassFinder.clearCache()
// 监听宿主的生命周期后调用
onAppLifecycle {
onCreate {
DexClassFinder.clearCache(context = this)
}
}
```
你还可以清除指定版本的宿主缓存。
> 示例如下
```kotlin
// 直接调用,在宿主的 appContext 为空时可能会失败,失败会打印警告信息
DexClassFinder.clearCache(versionName = "1.0", versionCode = 1)
// 监听宿主的生命周期后调用
onAppLifecycle {
onCreate {
DexClassFinder.clearCache(context = this, versionName = "1.0", versionCode = 1)
}
}
```
#### 多重查找
如果你需要使用固定的条件同时查找一组 `Class`,那么你只需要使用 `all` 或 `waitAll` 方法来得到结果。
```kotlin
// 同步查找,使用 all 得到条件全部查找到的结果
searchClass {
// ...
}.all().forEach { clazz ->
// 得到每个结果
}
// 同步查找,使用 all { ... } 遍历每个结果
searchClass {
// ...
}.all { clazz ->
// 得到每个结果
}
// 异步查找,使用 waitAll 得到条件全部查找到的结果
searchClass(async = true) {
// ...
}.waitAll { classes ->
classes.forEach {
// 得到每个结果
}
}
```
::: tip
更多功能请参考 [ClassLoader.searchClass](../public/com/highcapable/yukihookapi/hook/factory/ReflectionFactory#classloader-searchclass-ext-method)、[PackageParam.searchClass](../public/com/highcapable/yukihookapi/hook/param/PackageParam#searchclass-method) 方法。
:::
## Member 扩展 ## Member 扩展