Update document file

This commit is contained in:
2022-05-01 09:52:53 +08:00
parent 8d226bc42d
commit dfa3362c35
30 changed files with 1957 additions and 164 deletions

View File

@@ -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` 不存在时发生的异常。
> 示例如下

View File

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

View File

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

View File

@@ -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 功能将失效。

View File

@@ -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)
```
打印的结果为如下所示。