From c655416a91f0a32cfe82b1384f5958cace891833 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 20 May 2023 04:32:39 +0200 Subject: [PATCH] fix(twitter): correctly resolve to integrations methods --- .../fingerprints/JsonHookPatchFingerprint.kt | 6 +- .../misc/hook/json/patch/JsonHookPatch.kt | 115 ++++++++++++------ .../misc/hook/patch/BaseHookPatchPatch.kt | 6 +- .../misc/hook/patch/ads/patch/HideAdsPatch.kt | 6 +- .../patch/HideRecommendedUsersPatch.kt | 9 +- 5 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonHookPatchFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonHookPatchFingerprint.kt index c2c31346..732e7fb6 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonHookPatchFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonHookPatchFingerprint.kt @@ -5,5 +5,9 @@ import org.jf.dexlib2.Opcode object JsonHookPatchFingerprint : MethodFingerprint( customFingerprint = { methodDef, _ -> methodDef.name == "" }, - opcodes = listOf(Opcode.IGET_OBJECT) + opcodes = listOf( + Opcode.INVOKE_INTERFACE, // Add dummy hook to hooks list. + // Add hooks to the hooks list. + Opcode.INVOKE_STATIC // Call buildList. + ) ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/patch/JsonHookPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/patch/JsonHookPatch.kt index c604c681..8a14dc9c 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/patch/JsonHookPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/patch/JsonHookPatch.kt @@ -5,13 +5,18 @@ import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult -import app.revanced.patcher.patch.* +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultError +import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.annotations.RequiresIntegrations import app.revanced.patches.twitter.misc.hook.json.fingerprints.JsonHookPatchFingerprint import app.revanced.patches.twitter.misc.hook.json.fingerprints.JsonInputStreamFingerprint import app.revanced.patches.twitter.misc.hook.json.fingerprints.LoganSquareFingerprint +import java.io.Closeable import java.io.InvalidClassException @Name("json-hook") @@ -20,16 +25,16 @@ import java.io.InvalidClassException @RequiresIntegrations class JsonHookPatch : BytecodePatch( listOf(LoganSquareFingerprint) -) { +), Closeable { override fun execute(context: BytecodeContext): PatchResult { - // Make sure the integrations are present. - val jsonHookPatch = context.findClass { it.type == JSON_HOOK_PATCH_CLASS_DESCRIPTOR } - ?: return PatchResultError("Could not find integrations.") + JsonHookPatchFingerprint.also { + // Make sure the integrations are present. + val jsonHookPatch = context.findClass { classDef -> classDef.type == JSON_HOOK_PATCH_CLASS_DESCRIPTOR } + ?: throw PatchResultError("Could not find integrations.") - // Allow patch to inject hooks into the patches integrations. - jsonHookPatchFingerprintResult = JsonHookPatchFingerprint.also { - it.resolve(context, jsonHookPatch.immutableClass) - }.result ?: return PatchResultError("Unexpected integrations.") + if (!it.resolve(context, jsonHookPatch.immutableClass)) + throw PatchResultError("Unexpected integrations.") + }.let { hooks = JsonHookPatchHook(it) } // Conveniently find the type to hook a method in, via a named field. val jsonFactory = LoganSquareFingerprint.result @@ -64,30 +69,10 @@ class JsonHookPatch : BytecodePatch( * * @param context The [BytecodeContext] of the current patch. * @param descriptor The class descriptor of the hook. + * @throws ClassNotFoundException If the class could not be found. */ - internal class Hook(context: BytecodeContext, private val descriptor: String) { - private var added = false - - /** - * Add the hook. - */ - internal fun add() { - if (added) return - - jsonHookPatchFingerprintResult.apply { - mutableMethod.apply { - addInstructions( - scanResult.patternScanResult!!.startIndex, - """ - sget-object v1, $descriptor->INSTANCE:$descriptor - invoke-virtual {v0, v1}, Lkotlin/collections/builders/ListBuilder;->add(Ljava/lang/Object;)Z - """ - ) - } - } - - added = true - } + internal class Hook(context: BytecodeContext, internal val descriptor: String) { + internal var added = false init { context.findClass { it.type == descriptor }?.let { @@ -102,15 +87,67 @@ class JsonHookPatch : BytecodePatch( } } - private companion object { - const val JSON_HOOK_CLASS_NAMESPACE = "app/revanced/twitter/patches/hook/json" + /** + * A hook for the [JsonHookPatch]. + * + * @param jsonHookPatchFingerprint The [JsonHookPatchFingerprint] to hook. + */ + internal class JsonHookPatchHook(jsonHookPatchFingerprint: MethodFingerprint): Closeable { + private val jsonHookPatchFingerprintResult = jsonHookPatchFingerprint.result!! + private val jsonHookPatchIndex = jsonHookPatchFingerprintResult.scanResult.patternScanResult!!.endIndex - const val JSON_HOOK_PATCH_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/JsonHookPatch;" + /** + * Add a hook to the [JsonHookPatch]. + * Will not add the hook if it's already added. + * + * @param hook The [Hook] to add. + */ + fun addHook(hook: Hook) { + if (hook.added) return - const val BASE_PATCH_CLASS_NAME = "BaseJsonHook" + jsonHookPatchFingerprintResult.mutableMethod.apply { + // Insert hooks right before calling buildList. + val insertIndex = jsonHookPatchIndex - const val JSON_HOOK_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/$BASE_PATCH_CLASS_NAME;" + addInstructions( + insertIndex, + """ + sget-object v1, ${hook.descriptor}->INSTANCE:${hook.descriptor} + invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z + """ + ) + } - private lateinit var jsonHookPatchFingerprintResult: MethodFingerprintResult + hook.added = true + } + + override fun close() { + // Remove hooks.add(dummyHook). + jsonHookPatchFingerprintResult.mutableMethod.apply { + val addDummyHookIndex = jsonHookPatchIndex - 2 + + removeInstructions(addDummyHookIndex, 2) + } + } } + + override fun close() = hooks.close() + + internal companion object { + private const val JSON_HOOK_CLASS_NAMESPACE = "app/revanced/twitter/patches/hook/json" + + private const val JSON_HOOK_PATCH_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/JsonHookPatch;" + + private const val BASE_PATCH_CLASS_NAME = "BaseJsonHook" + + private const val JSON_HOOK_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/$BASE_PATCH_CLASS_NAME;" + + /** + * The [JsonHookPatchHook] of the [JsonHookPatch]. + * + * @see JsonHookPatchHook + */ + internal lateinit var hooks: JsonHookPatchHook + } + } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/BaseHookPatchPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/BaseHookPatchPatch.kt index f2dec2a2..4b71bc2a 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/BaseHookPatchPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/BaseHookPatchPatch.kt @@ -4,13 +4,13 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.patch.PatchResultSuccess -import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patches.twitter.misc.hook.json.patch.JsonHookPatch -@DependsOn([JsonHookPatch::class]) abstract class BaseHookPatchPatch(private val hookClassDescriptor: String) : BytecodePatch() { override fun execute(context: BytecodeContext) = try { - PatchResultSuccess().also { JsonHookPatch.Hook(context, hookClassDescriptor).add() } + JsonHookPatch.hooks.addHook(JsonHookPatch.Hook(context, hookClassDescriptor)) + + PatchResultSuccess() } catch (ex: Exception) { PatchResultError(ex) } diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/ads/patch/HideAdsPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/ads/patch/HideAdsPatch.kt index 6273a26d..36b54601 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/ads/patch/HideAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/ads/patch/HideAdsPatch.kt @@ -15,8 +15,4 @@ import app.revanced.patches.twitter.misc.hook.patch.ads.annotations.HideAdsCompa @Description("Hides ads.") @HideAdsCompatibility @Version("0.0.1") -class HideAdsPatch : BaseHookPatchPatch(HOOK_CLASS_DESCRIPTOR) { - private companion object { - const val HOOK_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/hook/patch/ads/AdsHook;" - } -} \ No newline at end of file +class HideAdsPatch : BaseHookPatchPatch("Lapp/revanced/twitter/patches/hook/patch/ads/AdsHook;") \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/recommendation/patch/HideRecommendedUsersPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/recommendation/patch/HideRecommendedUsersPatch.kt index a6ea294f..d38ab489 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/recommendation/patch/HideRecommendedUsersPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/recommendation/patch/HideRecommendedUsersPatch.kt @@ -15,9 +15,6 @@ import app.revanced.patches.twitter.misc.hook.patch.recommendation.annotations.H @Description("Hides recommended users.") @HideRecommendedUsersCompatibility @Version("0.0.1") -class HideRecommendedUsersPatch : BaseHookPatchPatch(HOOK_CLASS_DESCRIPTOR) { - private companion object { - const val HOOK_CLASS_DESCRIPTOR = - "Lapp/revanced/twitter/patches/hook/patch/recommendation/RecommendedUsersHook;" - } -} \ No newline at end of file +class HideRecommendedUsersPatch : BaseHookPatchPatch( + "Lapp/revanced/twitter/patches/hook/patch/recommendation/RecommendedUsersHook;" +) \ No newline at end of file