diff options
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.kt | 108 |
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 } |