mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 10:45:47 +08:00
Update document file
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
```
|
||||
Host Environment
|
||||
└── YukiHookCreater
|
||||
└── YukiMemberHookCreater
|
||||
└── Class
|
||||
└── MemberHookCreater
|
||||
└── Member
|
||||
@@ -19,6 +19,15 @@ Host Environment
|
||||
├── Before
|
||||
└── After
|
||||
...
|
||||
YukiResourcesHookCreater
|
||||
└── Resources
|
||||
└── ResourcesHookCreater
|
||||
└── Drawable
|
||||
└── Replace
|
||||
ResourcesHookCreater
|
||||
└── Layout
|
||||
└── Inject
|
||||
...
|
||||
```
|
||||
|
||||
> 上方的结构换做代码将可写为如下形式。
|
||||
@@ -37,6 +46,14 @@ TargetClass.hook {
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
// Your code here.
|
||||
}
|
||||
replaceTo(...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||
@@ -51,6 +68,10 @@ TargetClass.hook {
|
||||
|
||||
## 一个简单的 Hook 例子
|
||||
|
||||
> 这里给出了 Hook APP、Hook 系统框架与 Hook Resources 的例子,可供参考。
|
||||
|
||||
### Hook APP
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中的 `onCreate` 方法并弹出一个对话框。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
@@ -171,6 +192,97 @@ TestClass.hook {
|
||||
}
|
||||
```
|
||||
|
||||
### Hook 系统框架
|
||||
|
||||
在 `YukiHookAPI` 中,Hook 系统框架的实现非常简单。
|
||||
|
||||
假设我们要全局 Hook 一个 `Activity` 的 `onCreate` 事件
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这样就实现了上述的 Hook 功能。
|
||||
|
||||
!> `loadZygote` 与 `loadApp(name = "android")` 有直接性区别,`loadZygote` 会在 `initZygote` 中装载,若要 Hook 系统框架,建议使用 `loadZygote`。
|
||||
|
||||
### Hook Resources
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中 `string` 类型的 `app_name` 内容替换为 `123`。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "app_name"
|
||||
string()
|
||||
}
|
||||
replaceTo("123")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若当前 APP 使用 `app_name` 设置了标题栏文本,则它就会变成我们的 `123`。
|
||||
|
||||
你还可以使用当前 Xposed 模块的 Resources 替换 Hook APP 的 Resources。
|
||||
|
||||
假设,我们要继续 Hook `com.android.browser` 中 `mipmap` 类型的 `ic_launcher`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至此目标 APP 的图标将会被替换为我们设置的图标。
|
||||
|
||||
若你想替换系统框架的资源,同样也可以这样实现,只需要把 `loadApp` 换成 `loadZygote` 即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [ResourcesHookCreater](api/document?id=resourceshookcreater-class)。
|
||||
|
||||
## 异常处理
|
||||
|
||||
> `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
|
||||
@@ -193,6 +305,20 @@ injectMember {
|
||||
}
|
||||
```
|
||||
|
||||
在 Resources Hook 时此方法同样适用。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
// Your code here.
|
||||
}.result {
|
||||
// 处理 Hook 时的任意异常
|
||||
onHookingFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
你还可以处理 Hook 的 `Class` 不存在时发生的异常。
|
||||
|
||||
> 示例如下
|
||||
|
@@ -62,6 +62,8 @@
|
||||
|
||||
现在,我们只需要编写少量的代码,一切时间开销和花费交给自动化处理。
|
||||
|
||||
借助 `Kotlin` 优雅的 `lambda` 写法以及 `YukiHookAPI`,可以让你的 Hook 逻辑更加美观清晰。
|
||||
|
||||
> 示例如下
|
||||
|
||||
<!-- tabs:start -->
|
||||
@@ -70,9 +72,34 @@
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class MainHook : IYukiHookXposedInit {
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "sym_def_app_icon"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
@@ -88,6 +115,15 @@ class MainHook : IYukiHookXposedInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,14 +132,16 @@ class MainHook : IYukiHookXposedInit {
|
||||
#### **Xposed API**
|
||||
|
||||
```kotlin
|
||||
class MainHook : IXposedHookLoadPackage {
|
||||
class HookEntry : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
if (lpparam.packageName == "com.android.browser")
|
||||
XposedHelpers.findAndHookMethod(
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
XResources.setSystemWideReplacement("android", "mipmap", "sym_def_app_icon", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
lpparam.classLoader,
|
||||
"onCreate",
|
||||
null, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
@@ -115,14 +153,36 @@ class MainHook : IXposedHookLoadPackage {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
if (lpparam.packageName == "com.android.browser")
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
lpparam.classLoader, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
if (resparam.packageName == "com.android.browser")
|
||||
resparam.res.setReplacement("com.android.browser", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
是的,你没有看错,仅仅就需要这几行代码,就一切安排妥当。
|
||||
是的,你没有看错,仅仅就需要这些代码,就能完全取代 Xposed API 实现同样的功能。
|
||||
|
||||
代码量少,逻辑清晰,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
|
||||
现在,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
|
||||
|
||||
## 支持的 Hook 框架
|
||||
|
||||
@@ -131,7 +191,7 @@ class MainHook : IXposedHookLoadPackage {
|
||||
| Hook Framework | ST | Describe |
|
||||
| --------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------- |
|
||||
| [LSPosed](https://github.com/LSPosed/LSPosed) | ✅ | 多场景下稳定使用 |
|
||||
| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ✅ | 部分兼容 |
|
||||
| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ❎ | 已停止维护,不再推荐使用 |
|
||||
| [Pine](https://github.com/canyie/pine) | ⭕ | 可以使用 |
|
||||
| [SandHook](https://github.com/asLody/SandHook) | ⭕ | 可以使用 |
|
||||
| [Whale](https://github.com/asLody/whale) | ⭕ | 需要 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale) |
|
||||
|
@@ -22,15 +22,28 @@ override fun onHook() = encase {
|
||||
appInfo
|
||||
// 得到宿主 Application 生命周期
|
||||
appContext
|
||||
// 创建 Hook
|
||||
findClass("com.demo.Test").hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(BooleanType)
|
||||
// Hook 指定的 APP
|
||||
loadApp(name = "com.demo.test") {
|
||||
// Class Hook
|
||||
findClass("com.demo.test.TestClass").hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(BooleanType)
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
// Resources Hook (固定用法)
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +53,12 @@ override fun onHook() = encase {
|
||||
#### **Xposed API**
|
||||
|
||||
```kotlin
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
lpparam.packageName
|
||||
@@ -47,12 +66,20 @@ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
lpparam.applicationInfo
|
||||
// 得到宿主 Application 生命周期
|
||||
AndroidAppHelper.currentApplication()
|
||||
// Class Hook
|
||||
if(lpparam.packageName == "com.demo.test")
|
||||
XposedHelpers.findAndHookMethod("com.demo.test.TestClass", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
resparam.packageName
|
||||
// 创建 Hook
|
||||
XposedHelpers.findAndHookMethod("com.demo.Test", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
resparam.res.setReplacement("com.demo.test", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -101,7 +101,7 @@ dependencies {
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class MainHook : IYukiHookXposedInit {
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = YukiHookAPI.encase {
|
||||
// Your code here.
|
||||
@@ -153,4 +153,4 @@ override fun attachBaseContext(base: Context?) {
|
||||
|
||||
### 特别说明
|
||||
|
||||
!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 功能将失效。
|
||||
!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ 以及 Resources Hook 功能将失效。
|
@@ -11,7 +11,26 @@
|
||||
```java
|
||||
package com.demo;
|
||||
|
||||
public class Test {
|
||||
public class BaseTest {
|
||||
|
||||
public BaseTest() {
|
||||
// ...
|
||||
}
|
||||
|
||||
public BaseTest(boolean isInit) {
|
||||
// ...
|
||||
}
|
||||
|
||||
private void doBaseTask(String taskName) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
package com.demo;
|
||||
|
||||
public class Test extends BaseTest {
|
||||
|
||||
public Test() {
|
||||
// ...
|
||||
@@ -61,7 +80,7 @@ public class Test {
|
||||
|
||||
### 查询与反射调用
|
||||
|
||||
假设我们要得到 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。
|
||||
假设我们要得到 `Test`(以下统称“当前 `Class`”)的 `doTask` 方法并执行,通常情况下,我们可以使用标准的反射 API 去查询这个方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
@@ -178,6 +197,54 @@ Test::class.java.method {
|
||||
}.get(instance) // 得到这个方法
|
||||
```
|
||||
|
||||
### 在父类查询
|
||||
|
||||
你会注意到 `Test` 继承于 `BaseTest`,现在我们想得到 `BaseTest` 的 `doBaseTask` 方法,在不知道父类名称的情况下,要怎么做呢?
|
||||
|
||||
参照上面的查询条件,我们只需要在查询条件中加入一个 `superClass` 即可实现这个功能。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val instance = Test()
|
||||
// 使用 YukiHookAPI 调用并执行
|
||||
Test::class.java.method {
|
||||
name = "doBaseTask"
|
||||
param(StringType)
|
||||
// 只需要添加这个条件
|
||||
superClass()
|
||||
}.get(instance).call("task_name")
|
||||
```
|
||||
|
||||
这个时候我们就可以在父类中取到这个方法了。
|
||||
|
||||
`superClass` 有一个参数为 `isOnlySuperClass`,设置为 `true` 后,可以跳过当前 `Class` 仅查询当前 `Class` 的父类。
|
||||
|
||||
由于我们现在已知 `doBaseTask` 方法只存在于父类,可以加上这个条件节省查询时间。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val instance = Test()
|
||||
// 使用 YukiHookAPI 调用并执行
|
||||
Test::class.java.method {
|
||||
name = "doBaseTask"
|
||||
param(StringType)
|
||||
// 加入一个查询条件
|
||||
superClass(isOnlySuperClass = true)
|
||||
}.get(instance).call("task_name")
|
||||
```
|
||||
|
||||
这个时候我们同样可以得到父类中的这个方法。
|
||||
|
||||
`superClass` 一旦设置就会自动循环向后查找全部继承的父类中是否有这个方法,直到查询到目标没有父类(继承关系为 `java.lang.Object`)为止。
|
||||
|
||||
更多用法可参考 [superClass 方法](api/document?id=superclass-method)。
|
||||
|
||||
!> 当前查询的 `Method` 除非指定 `superClass` 条件,否则只能查询到当前 `Class` 的 `Method`。
|
||||
|
||||
### 静态字节码
|
||||
|
||||
有些方法和变量在 `Class` 中是静态的实现,这个时候,我们不需要传入实例就可以调用它们。
|
||||
@@ -347,6 +414,23 @@ instance.current {
|
||||
}
|
||||
```
|
||||
|
||||
我们还可以用 `superClass` 调用当前 `Class` 父类的方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 假设这就是这个 Class 的实例
|
||||
val instance = Test()
|
||||
// 假设这个 Class 是不能被直接得到的
|
||||
instance.current {
|
||||
// 执行父类的 doBaseTask 方法
|
||||
superClass().method {
|
||||
name = "doBaseTask"
|
||||
param(StringType)
|
||||
}.call("task_name")
|
||||
}
|
||||
```
|
||||
|
||||
问题又来了,我想使用反射的方式创建如下的实例并调用其中的方法,该怎么做呢?
|
||||
|
||||
> 示例如下
|
||||
@@ -768,9 +852,9 @@ loggerE(msg = "This is an error")
|
||||
|
||||
```kotlin
|
||||
// 假设这就是被抛出的异常
|
||||
val e = Throwable(...)
|
||||
val throwable = Throwable(...)
|
||||
// 打印日志
|
||||
loggerE(msg = "This is an error", e = e)
|
||||
loggerE(msg = "This is an error", e = throwable)
|
||||
```
|
||||
|
||||
打印的结果为如下所示。
|
||||
|
Reference in New Issue
Block a user