aboutsummaryrefslogtreecommitdiff
path: root/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
diff options
context:
space:
mode:
Diffstat (limited to 'agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt')
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt108
1 files changed, 61 insertions, 47 deletions
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
index 92106e14..ff68ad94 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
+++ b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
@@ -18,46 +18,65 @@ package com.code_intelligence.jazzer.instrumentor
import com.code_intelligence.jazzer.api.HookType
import com.code_intelligence.jazzer.api.MethodHook
-import com.code_intelligence.jazzer.api.MethodHooks
import com.code_intelligence.jazzer.utils.descriptor
import java.lang.invoke.MethodHandle
import java.lang.reflect.Method
import java.lang.reflect.Modifier
-class Hook private constructor(hookMethod: Method, annotation: MethodHook) {
- // Allowing arbitrary exterior whitespace in the target class name allows for an easy workaround
- // for mangled hooks due to shading applied to hooks.
- private val targetClassName = annotation.targetClassName.trim()
- val targetMethodName = annotation.targetMethod
- val targetMethodDescriptor = annotation.targetMethodDescriptor.takeIf { it.isNotEmpty() }
- val hookType = annotation.type
-
- val targetInternalClassName = targetClassName.replace('.', '/')
- private val targetReturnTypeDescriptor = targetMethodDescriptor?.let { extractReturnTypeDescriptor(it) }
- private val targetWrappedReturnTypeDescriptor = targetReturnTypeDescriptor?.let { getWrapperTypeDescriptor(it) }
-
- private val hookClassName: String = hookMethod.declaringClass.name
- val hookInternalClassName = hookClassName.replace('.', '/')
- val hookMethodName: String = hookMethod.name
- val hookMethodDescriptor = hookMethod.descriptor
+class Hook private constructor(
+ private val targetClassName: String,
+ val hookType: HookType,
+ val targetMethodName: String,
+ val targetMethodDescriptor: String?,
+ val additionalClassesToHook: List<String>,
+ val targetInternalClassName: String,
+ private val targetReturnTypeDescriptor: String?,
+ private val targetWrappedReturnTypeDescriptor: String?,
+ private val hookClassName: String,
+ val hookInternalClassName: String,
+ val hookMethodName: String,
+ val hookMethodDescriptor: String
+) {
override fun toString(): String {
- return "$hookType $targetClassName.$targetMethodName: $hookClassName.$hookMethodName"
+ return "$hookType $targetClassName.$targetMethodName: $hookClassName.$hookMethodName $additionalClassesToHook"
}
companion object {
+ fun createAndVerifyHook(hookMethod: Method, hookData: MethodHook, className: String): Hook {
+ return createHook(hookMethod, hookData, className).also {
+ verify(hookMethod, it)
+ }
+ }
- fun verifyAndGetHook(hookMethod: Method, hookData: MethodHook): Hook {
- // Verify the annotation type and extract information for debug statements.
- val potentialHook = Hook(hookMethod, hookData)
+ private fun createHook(hookMethod: Method, annotation: MethodHook, targetClassName: String): Hook {
+ val targetReturnTypeDescriptor = annotation.targetMethodDescriptor
+ .takeIf { it.isNotBlank() }?.let { extractReturnTypeDescriptor(it) }
+ val hookClassName: String = hookMethod.declaringClass.name
+ return Hook(
+ targetClassName = targetClassName,
+ hookType = annotation.type,
+ targetMethodName = annotation.targetMethod,
+ targetMethodDescriptor = annotation.targetMethodDescriptor.takeIf { it.isNotBlank() },
+ additionalClassesToHook = annotation.additionalClassesToHook.asList(),
+ targetInternalClassName = targetClassName.replace('.', '/'),
+ targetReturnTypeDescriptor = targetReturnTypeDescriptor,
+ targetWrappedReturnTypeDescriptor = targetReturnTypeDescriptor?.let { getWrapperTypeDescriptor(it) },
+ hookClassName = hookClassName,
+ hookInternalClassName = hookClassName.replace('.', '/'),
+ hookMethodName = hookMethod.name,
+ hookMethodDescriptor = hookMethod.descriptor
+ )
+ }
+ private fun verify(hookMethod: Method, potentialHook: Hook) {
// Verify the hook method's modifiers (public static).
require(Modifier.isPublic(hookMethod.modifiers)) { "$potentialHook: hook method must be public" }
require(Modifier.isStatic(hookMethod.modifiers)) { "$potentialHook: hook method must be static" }
// Verify the hook method's parameter count.
val numParameters = hookMethod.parameters.size
- when (hookData.type) {
+ when (potentialHook.hookType) {
HookType.BEFORE, HookType.REPLACE -> require(numParameters == 4) { "$potentialHook: incorrect number of parameters (expected 4)" }
HookType.AFTER -> require(numParameters == 5) { "$potentialHook: incorrect number of parameters (expected 5)" }
}
@@ -70,17 +89,18 @@ class Hook private constructor(hookMethod: Method, annotation: MethodHook) {
require(parameterTypes[3] == Int::class.javaPrimitiveType) { "$potentialHook: fourth parameter must have type int" }
// Verify the hook method's return type if possible.
- when (hookData.type) {
+ when (potentialHook.hookType) {
HookType.BEFORE, HookType.AFTER -> require(hookMethod.returnType == Void.TYPE) {
"$potentialHook: return type must be void"
}
HookType.REPLACE -> if (potentialHook.targetReturnTypeDescriptor != null) {
- val returnTypeDescriptor = hookMethod.returnType.descriptor
- if (potentialHook.targetReturnTypeDescriptor == "V") {
- require(returnTypeDescriptor == "V") { "$potentialHook: return type must be void to match targetMethodDescriptor" }
+ if (potentialHook.targetMethodName == "<init>") {
+ require(hookMethod.returnType.name == potentialHook.targetClassName) { "$potentialHook: return type must be ${potentialHook.targetClassName} to match target constructor" }
+ } else if (potentialHook.targetReturnTypeDescriptor == "V") {
+ require(hookMethod.returnType.descriptor == "V") { "$potentialHook: return type must be void" }
} else {
require(
- returnTypeDescriptor in listOf(
+ hookMethod.returnType.descriptor in listOf(
java.lang.Object::class.java.descriptor,
potentialHook.targetReturnTypeDescriptor,
potentialHook.targetWrappedReturnTypeDescriptor
@@ -92,28 +112,22 @@ class Hook private constructor(hookMethod: Method, annotation: MethodHook) {
}
}
- // AfterMethodHook only: Verify the type of the last parameter if known.
- if (hookData.type == HookType.AFTER && potentialHook.targetReturnTypeDescriptor != null) {
- require(
- parameterTypes[4] == java.lang.Object::class.java ||
- parameterTypes[4].descriptor == potentialHook.targetWrappedReturnTypeDescriptor
- ) {
- "$potentialHook: fifth parameter must have type Object or match the descriptor ${potentialHook.targetWrappedReturnTypeDescriptor}"
+ // AfterMethodHook only: Verify the type of the last parameter if known. Even if not
+ // known, it must not be a primitive value.
+ if (potentialHook.hookType == HookType.AFTER) {
+ if (potentialHook.targetReturnTypeDescriptor != null) {
+ require(
+ parameterTypes[4] == java.lang.Object::class.java ||
+ parameterTypes[4].descriptor == potentialHook.targetWrappedReturnTypeDescriptor
+ ) {
+ "$potentialHook: fifth parameter must have type Object or match the descriptor ${potentialHook.targetWrappedReturnTypeDescriptor}"
+ }
+ } else {
+ require(!parameterTypes[4].isPrimitive) {
+ "$potentialHook: fifth parameter must not be a primitive type, use a boxed type instead"
+ }
}
}
-
- return potentialHook
- }
- }
-}
-
-fun loadHooks(hookClass: Class<*>): List<Hook> {
- val hooks = mutableListOf<Hook>()
- for (method in hookClass.methods) {
- method.getAnnotation(MethodHook::class.java)?.let { hooks.add(Hook.verifyAndGetHook(method, it)) }
- method.getAnnotation(MethodHooks::class.java)?.let {
- it.value.forEach { hookAnnotation -> hooks.add(Hook.verifyAndGetHook(method, hookAnnotation)) }
}
}
- return hooks
}