Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions legacy/src/main/java/de/robv/android/xposed/XposedBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,17 @@ public static void deoptimizeMethod(Member deoptimizedMethod) {
*/
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
if (!(hookMethod instanceof Executable)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod);
throw new IllegalArgumentException("Only methods and constructors can be hooked, not " + hookMethod);
} else if (Modifier.isAbstract(hookMethod.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod);
throw new IllegalArgumentException(hookMethod + " is abstract: it has no body to hook. Hook the concrete override instead.");
} else if (hookMethod.getDeclaringClass().getClassLoader() == XposedBridge.class.getClassLoader()) {
throw new IllegalArgumentException("Do not allow hooking inner methods");
throw new IllegalArgumentException(hookMethod + " belongs to Vector itself. Hooking the framework would hook the code running your hook.");
} else if (hookMethod.getDeclaringClass() == Method.class && hookMethod.getName().equals("invoke")) {
throw new IllegalArgumentException("Cannot hook Method.invoke");
throw new IllegalArgumentException("Method.invoke cannot be hooked: Vector calls it to run the original of every hooked method, so a hook here would call itself forever.");
} else if (hookMethod.getDeclaringClass() == Object.class && hookMethod.getName().equals("getClass")) {
// Since AGP 9, R8 compiles Kotlin null checks into Object.getClass(), so the dispatch
// calls it entering every hooked method; a hook here re-enters the dispatch. See #798.
throw new IllegalArgumentException("Object.getClass cannot be hooked: Vector's dispatch calls it entering every hooked method, so a hook here would call itself forever.");
}

if (callback == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,41 @@ class VectorHookBuilder(

override fun intercept(hooker: Hooker): HookHandle {
if (Modifier.isAbstract(origin.modifiers)) {
throw IllegalArgumentException("Cannot hook abstract methods: $origin")
throw IllegalArgumentException(
"$origin is abstract: it has no body to hook. Hook the concrete override instead."
)
} else if (origin.declaringClass.classLoader == VectorHookBuilder::class.java.classLoader) {
throw IllegalArgumentException("Do not allow hooking inner methods")
throw IllegalArgumentException(
"$origin belongs to Vector itself. Hooking the framework would hook the code running your hook."
)
} else if (
origin is Method &&
origin.declaringClass == Method::class.java &&
origin.name == "invoke"
) {
throw IllegalArgumentException("Cannot hook Method.invoke")
throw IllegalArgumentException(
"Method.invoke cannot be hooked: Vector calls it to run the original of every hooked method, so a hook here would call itself forever."
)
} else if (
origin is Method &&
origin.declaringClass == Constructor::class.java &&
origin.name == "newInstance"
) {
// Named alongside Method.invoke by the API: the framework reflects through both, so
// hooking either recurses into the hook dispatch.
throw IllegalArgumentException("Cannot hook Constructor.newInstance")
throw IllegalArgumentException(
"Constructor.newInstance cannot be hooked: Vector reflects through it the same way it does Method.invoke, so a hook here would recurse."
)
} else if (
origin is Method &&
origin.declaringClass == Any::class.java &&
origin.name == "getClass"
) {
// The compiler, not the framework, is what makes this recurse: since AGP 9 R8 compiles
// Kotlin null checks into Object.getClass(), so the dispatch calls it entering every
// hooked method. See #798. If you meant a getClass override on another class, hook that
// class; Class.getMethods() lists the inherited one, which is not what you want.
throw IllegalArgumentException(
"Object.getClass cannot be hooked: Vector's dispatch calls it entering every hooked method, so a hook here would call itself forever."
)
}

// Resolve DEFAULT here rather than at throw time: the record is stored natively and
Expand Down
Loading