YukiHookAPI Documentation
repository·master·Indexed 24 days ago
https://github.com/highcapable/yukihookapiA Kotlin-based Hook API and Xposed Module solution that rebuilds the Xposed API with modern Kotlin features. It provides an efficient interface for Xposed development, utilizing KavaRef as its reflection driver. The library includes features such as the encase lambda for hooking logic, YukiBaseHooker for structured projects, Zygote and Resource hooking, and support for both Xposed modules and standalone Hook API integration.
What's inside YukiHookAPI
- Yuki Hook API is a Kotlin-based library designed to provide an efficient interface for the Xposed API. It extends the standard Xposed capabilities with rich function extensions, making it easier to develop Xposed Modules. It uses KavaRef as a powerful driver for its reflection API.
What is YukiHookAPI and its purpose
masterYukiHookAPI is a Kotlin-focused API framework designed to provide syntactic sugar and complete usage encapsulation for Xposed module development.
Historically, Xposed development relied heavily on
XposedHelpers, which lacks modern Kotlin ergonomics. YukiHookAPI aims to simplify the development process by providing a more capable and easy-to-understand API. The project's goal is to adapt to various third-party Hook Frameworks (like LSPosed) while maintaining a consistent API surface, helping developers avoid the complexities of low-level Xposed development.Use the `current` method for concise reflection calls
masterInstead of repeatedly passing an instance to
.get(instance), you can use thecurrentextension on an instance to create a scoped call space. This allows you to perform multiple reflection operations on the same object concisely.instance.current { ... }: Provides a lambda scope where you can callmethod { ... }orfield { ... }directly.instance.current(): Returns aCurrentClassobject that can be used for chained calls.superClass(): Within acurrentblock, usesuperClass()to access members of the parent class.field { ... }.current(): Allows you to jump from a field to the instance that field belongs to.
Note: You cannot perform inline calls like
instance.current().current()becausecurrent()returns theCurrentClassobject itself.val instance = Test() instance.current { // Execute a method on the current instance method { name = "doTask" param(StringClass) }.call("task_name") // Execute a method on the parent class superClass().method { name = "doBaseTask" param(StringClass) }.call("task_name") // Get a field value val name = method { name = "getName" }.string() }Use advanced search conditions for methods
masterWhen finding methods, you can use several strategies to handle complex or ambiguous signatures:
- Parameter Count: If you don't know the exact types, use
paramCountto match by the number of arguments. - Vague Types: Use
VagueTypeto specify a parameter type when you only know some of the types in a signature. - Conditional Logic: Use
param { ... }to provide a custom predicate. The lambda provides an array ofClassobjects representing the parameter types. The predicate must return aBoolean.
Example of
param { ... }:Test::class.java.method { name = "release" param { it[0] == StringClass && it[2] == BooleanType } }.get(instance)- Parameter Count: If you don't know the exact types, use
Handle duplicate hooks in YukiHookAPI 1.3.x
masterYukiHookAPI
1.3.0and later no longer restrict duplicate hooks to the same method; you can now hook the same method multiple times.As a result:
- The
onAlreadyHookedmethod within thehook { ... }block is deprecated and will no longer be called. - If your logic requires handling duplicate hooks, you must implement that logic manually.
- The
Create a structured Hooker using YukiBaseHooker
masterFor large-scale projects, extend
YukiBaseHookerto classify your hooking logic. You can then pass these custom hookers intoencaseas a variable array ofYukiBaseHookerobjects.Key Rules:
- Use
object(singleton) to create child hookers whenever possible. - Do not call
encaseagain inside theonHookmethod of aYukiBaseHooker; instead, write your hook code directly withinonHookusingloadApp,loadZygote, orloadSystem. - You can use
loadHooker(hooker)within aYukiBaseHookerto load other hookers in a layered approach.
- Use
Handle YukiHookDataChannel data size limits
masterWhen using
YukiHookDataChannelto send broadcast data, the system has a limit on the size of the data sent. By default, YukiHookAPI attempts to segment data for common types likeList,Map,Set, andString.If you attempt to send a data type that is too large and does not support automatic segmentation, you will receive an error:
YukiHookDataChannel cannot send this data key of "KEY" type TYPE, because it is too large....Recommendation: Do not bypass this limit. If you must, use
allowSendTooLargeData, but be aware that this can cause the host app to crash if the system refuses the oversized broadcast.Migrate XposedHelpers reflection to YukiHookAPI or KavaRef
masterThe reflection API in YukiHookAPI (deprecated in
1.3.0) differs fromXposedHelpers. WhileXposedHelpers.callMethodautomatically searches superclasses, YukiHookAPI's reflection requires explicit instructions to search superclasses.Important: For versions
1.3.0and later, it is recommended to migrate to KavaRef.To search superclasses in YukiHookAPI: Use
.superClass()within the method search block or call the method directly on the superclass instance.Example of searching superclasses:
instance.current().method { name = "test" superClass() // Ensures search includes superclasses }.call("some string")val instance: A = ... instance.current().method { name = "test" // Note that you need to add this search condition to ensure it searches for methods in superclasses. superClass() }.call("some string") // Or directly call the superClass() method. instance.current().superClass()?.method { name = "test" } ?.call("some string")Handle Blocking Exceptions in YukiHookAPI
masterCertain exceptions in YukiHookAPI are 'Blocking Exceptions'. These will cause the application to stop running (Force Close), printElevel logs to the console, and cause the Hook process to terminate. Common causes include incorrect initialization ofencase, missing class definitions in the currentClassLoader, or attempting to accessappContextin an invalid lifecycle state.Understand the YukiHookAPI structure
masterYukiHookAPI follows a hierarchical structure for hooking. The primary entry points are the Host Environment, which branches into
YukiMemberHookCreatorfor class and member hooking, andYukiResourcesHookCreatorfor resource manipulation (e.g., replacing drawables or injecting layouts).In code, this is typically expressed by resolving a target class, selecting a method via a builder, and then defining
beforeandafterhooks.Handle varying class versions with RemedyPlan
masterWhen a host application has multiple versions where a method's signature (parameters) might change, use
RemedyPlan. This allows you to define multiple potential method signatures and execute logic once a match is found.Important: When using
RemedyPlan, do not use.get()to retrieve the method instance. Instead, use the.wait(instance)or.waitAll(instance)methods to execute the logic against a specific object instance.val instance = Test() // Use .remedys with .wait(instance) for single match logic Test::class.java.method { name = "doTask" emptyParam() }.remedys { method { name = "doTask" param(StringClass) }.onFind { // Found logic } method { name = "doTask" param(StringClass, IntType) }.onFind { // Found logic } }.wait(instance) { // Get the result } // Use .waitAll(instance) when using multiple find patterns Test::class.java.method { name = "doTask" emptyParam() }.remedys { method { name = "doTask" paramCount(0..1) }.onFind { // Found logic } method { name = "doTask" paramCount(1..2) }.onFind { // Found logic } }.waitAll(instance) { // Get the result }Understanding Xposed Hooking principles
masterXposed operates via parasitism. An Xposed Module follows the lifecycle of a Host (App) and completes its own lifecycle within that host's environment.
By injecting into the Host when it is running, you can use reflection to access the Host's methods, fields, and constructors. The
XposedBridgeprovides the hook operation, allowing you to:- Dynamically insert code before a method is executed.
- Dynamically insert code after a method is executed.
- Replace the target method entirely.
- Intercept the target method.