diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/extensions/MethodExtensions.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/extensions/MethodExtensions.kt index d4082d78c..b06e5af84 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/extensions/MethodExtensions.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/extensions/MethodExtensions.kt @@ -1,12 +1,8 @@ package app.revanced.patches.youtube.ad.general.bytecode.extensions -import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.softCompareTo import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import org.jf.dexlib2.builder.BuilderInstruction -import org.jf.dexlib2.builder.MutableMethodImplementation import org.jf.dexlib2.iface.Method import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction @@ -16,21 +12,6 @@ import org.jf.dexlib2.iface.reference.Reference import org.jf.dexlib2.immutable.reference.ImmutableMethodReference internal object MethodExtensions { - internal fun MutableMethodImplementation.insertBlocks( - startIndex: Int, - vararg blocks: List, - ) { - blocks.reversed().forEach { - this.addInstructions( - startIndex, it - ) - } - } - - internal fun MutableClass.addMethod(mutableMethod: MutableMethod) { - this.methods.add(mutableMethod) - } - internal fun MutableClass.findMutableMethodOf( method: Method ) = this.methods.first { diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt index 4cfb839c1..91ed4ed58 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt @@ -6,6 +6,9 @@ import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.impl.BytecodeData import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.patch.PatchResultSuccess @@ -14,23 +17,18 @@ import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.smali.toInstructions +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility -import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.addMethod import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.findMutableMethodOf -import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.insertBlocks import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.toDescriptor -import app.revanced.patches.youtube.ad.general.bytecode.utils.MethodUtils.createMutableMethod import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference import org.jf.dexlib2.Opcode -import org.jf.dexlib2.builder.MutableMethodImplementation -import org.jf.dexlib2.builder.instruction.* -import org.jf.dexlib2.iface.MethodImplementation -import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.builder.instruction.BuilderInstruction10x +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.formats.Instruction21c import org.jf.dexlib2.iface.instruction.formats.Instruction22c import org.jf.dexlib2.iface.instruction.formats.Instruction31i @@ -38,7 +36,6 @@ import org.jf.dexlib2.iface.instruction.formats.Instruction35c import org.jf.dexlib2.iface.reference.FieldReference import org.jf.dexlib2.iface.reference.MethodReference import org.jf.dexlib2.iface.reference.StringReference -import org.jf.dexlib2.immutable.reference.ImmutableMethodReference @Patch @DependsOn([ResourceMappingResourcePatch::class, IntegrationsPatch::class, SettingsPatch::class]) @@ -170,13 +167,6 @@ class GeneralBytecodeAdsPatch : BytecodePatch() { StringResource("revanced_adremover_paid_content_enabled_summary_on", "Paid content is hidden"), StringResource("revanced_adremover_paid_content_enabled_summary_off", "Paid content is shown") ), - SwitchPreference( - "revanced_adremover_suggested", - StringResource("revanced_adremover_suggested_enabled_title", "Remove personal suggestions"), - true, - StringResource("revanced_adremover_suggested_enabled_summary_on", "Personal suggestions are hidden"), - StringResource("revanced_adremover_suggested_enabled_summary_off", "Personal suggestions are shown") - ), SwitchPreference( "revanced_adremover_hide_suggestions", StringResource("revanced_adremover_hide_suggestions_enabled_title", "Hide suggestions"), @@ -329,72 +319,54 @@ class GeneralBytecodeAdsPatch : BytecodePatch() { } stringReferences[2] -> { // Litho ads - // create proxied method. val proxy = data.proxy(classDef) val proxiedClass = proxy.resolve() - // add getIsEmpty method - proxiedClass.addGetIsEmptyMethod() - - // get required method to patch and get references from val lithoMethod = getLithoMethod(proxiedClass) - ?: return PatchResultError("Could not find required litho method to patch.") - val lithoMethodImplementation = lithoMethod.implementation!! + ?: return PatchResultError("Could not find required Litho method to patch.") - // create and add getTemplateName method - val getTemplateMethod = - proxiedClass.createGetTemplateNameMethod(lithoMethodImplementation) - proxiedClass.addMethod(getTemplateMethod) + val instructionWithNeededDescriptor = + lithoMethod.implementation!!.instructions.indexOfFirst { + it.opcode == Opcode.INVOKE_STATIC_RANGE + } - val lithoInstructions = lithoMethodImplementation.instructions - val thisType = proxiedClass.type - val templateNameParameterType = getTemplateMethod.parameterTypes.first() + // get required descriptors + val createEmptyComponentDescriptor = + lithoMethod.instruction(instructionWithNeededDescriptor) + .toDescriptor() + val emptyComponentFieldDescriptor = + lithoMethod.instruction(instructionWithNeededDescriptor + 2) + .toDescriptor() - // get reference descriptors - val indexOfReference1 = lithoInstructions.indexOfFirst { - it.opcode == Opcode.INVOKE_STATIC_RANGE - } - val descriptor1 = - lithoInstructions.elementAt(indexOfReference1).toDescriptor() - val descriptor2 = lithoInstructions.elementAt(indexOfReference1 + 2) - .toDescriptor() + val pathBuilderAnchorFingerprint = object : MethodFingerprint( + opcodes = listOf( + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_OBJECT + ) + ) {} - // create label - val lithoRemoveLabel = lithoMethodImplementation.newLabelForIndex(0) + val pathBuilderScanResult = pathBuilderAnchorFingerprint.also { + it.resolve(data, lithoMethod, classDef) + }.result!!.scanResult.patternScanResult!! - // create branch instructions - val ifEqzFirstInstruction = - BuilderInstruction21t(Opcode.IF_EQZ, 0, lithoRemoveLabel) - val ifEqzSecondInstruction = - BuilderInstruction21t(Opcode.IF_EQZ, 1, lithoRemoveLabel) + val clobberedRegister = + (lithoMethod.instruction(pathBuilderScanResult.startIndex) as OneRegisterInstruction).registerA - // create blocks - val block1 = """ - invoke-static/range {p3}, $thisType->getTemplateName($templateNameParameterType)Ljava/lang/String; - move-result-object v0 - """.toInstructions(lithoMethod) - val block2 = """ - move-object/from16 v1, p3 - iget-object v2, v1, $templateNameParameterType->b:Ljava/nio/ByteBuffer; - invoke-static {v0, v2}, Lapp/revanced/integrations/patches/GeneralBytecodeAdsPatch;->containsAd(Ljava/lang/String;Ljava/nio/ByteBuffer;)Z - move-result v1 - """.toInstructions(lithoMethod) - val block3 = """ - move-object/from16 v2, p1 - invoke-static {v2}, $descriptor1 - move-result-object v0 - iget-object v0, v0, $descriptor2 - return-object v0 - """.toInstructions(lithoMethod) - - // insert blocks and branch instructions - lithoMethodImplementation.insertBlocks( - 0, - block1, - listOf(ifEqzFirstInstruction), - block2, - listOf(ifEqzSecondInstruction), - block3, + val insertIndex = pathBuilderScanResult.endIndex + 1 + lithoMethod.addInstructions( + insertIndex, // right after setting the component.pathBuilder field, + """ + invoke-static {v5}, Lapp/revanced/integrations/patches/GeneralBytecodeAdsPatch;->isAdComponent(Ljava/lang/StringBuilder;)Z + move-result v$clobberedRegister + if-eqz v$clobberedRegister, :not_an_ad + move-object/from16 v2, p1 + invoke-static {v2}, $createEmptyComponentDescriptor + move-result-object v0 + iget-object v0, v0, $emptyComponentFieldDescriptor + return-object v0 + """, + listOf(ExternalLabel("not_an_ad", lithoMethod.instruction(insertIndex))) ) } } @@ -414,142 +386,4 @@ class GeneralBytecodeAdsPatch : BytecodePatch() { instruction.opcode == Opcode.CONST && (instruction as Instruction31i).narrowLiteral == lithoConstant } ?: false } - - private fun MutableClass.addGetIsEmptyMethod() { - val getIsEmptyImplementation = MutableMethodImplementation(1) - - // create target instructions - val firstTargetInstruction = BuilderInstruction11n(Opcode.CONST_4, 0, 1) - val secondTargetInstruction = BuilderInstruction11n(Opcode.CONST_4, 0, 0) - - // add instructions to the instruction list - getIsEmptyImplementation.addInstructions( - 0, listOf( - // BuilderInstruction21t(Opcode.IF_EQZ, 0, first), - BuilderInstruction35c( - Opcode.INVOKE_VIRTUAL, - 1, - 0, - 0, - 0, - 0, - 0, - ImmutableMethodReference("Ljava/lang/String;", "isEmpty", null, "Z") - ), - BuilderInstruction11x(Opcode.MOVE_RESULT, 0), - // BuilderInstruction21t(Opcode.IF_EQZ, 0, second), - // BuilderInstruction10t(Opcode.GOTO, first), - secondTargetInstruction, - BuilderInstruction11x(Opcode.RETURN, 0), - firstTargetInstruction, - BuilderInstruction11x(Opcode.RETURN, 0), - ) - ) - - val getIsEmptyInstructions = getIsEmptyImplementation.instructions - - // create labels for the target instructions - val firstLabel = - getIsEmptyImplementation.newLabelForIndex(getIsEmptyInstructions.indexOf(firstTargetInstruction)) - val secondLabel = - getIsEmptyImplementation.newLabelForIndex(getIsEmptyInstructions.indexOf(secondTargetInstruction)) - - // create branch instructions to the labels - val ifEqzFirstInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 0, firstLabel) - val ifEqzSecondInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 0, secondLabel) - val gotoInstruction = BuilderInstruction10t(Opcode.GOTO, firstLabel) - - // insert remaining branch instructions, order of adding those instructions is important - getIsEmptyImplementation.addInstructions( - 2, listOf( - ifEqzSecondInstruction, gotoInstruction - ) - ) - getIsEmptyImplementation.addInstruction( - 0, ifEqzFirstInstruction - ) - - this.addMethod( - createMutableMethod( - this.type, "getIsEmpty", "Z", "Ljava/lang/String;", getIsEmptyImplementation - ) - ) - } - - private fun MutableClass.createGetTemplateNameMethod(lithoMethodImplementation: MethodImplementation): MutableMethod { - var counter = 1 - val descriptors = buildList { - for (instruction in lithoMethodImplementation.instructions) { - if (instruction !is ReferenceInstruction) continue - if (counter++ > 4) break - - add(instruction.toDescriptor()) - } - } - - val getTemplateNameImplementation = MutableMethodImplementation(2) - - // create code blocks - val block1 = """ - invoke-virtual {p0}, ${descriptors[0]} - move-result-object p0 - const v0, $lithoConstant - invoke-static {p0, v0}, ${descriptors[1]} - move-result-object p0 - """.toInstructions() - val block2 = """ - invoke-static {p0}, ${descriptors[2]} - move-result-object p0 - invoke-virtual {p0}, ${descriptors[3]} - move-result-object v0 - invoke-static {v0}, ${this.type}->getIsEmpty(Ljava/lang/String;)Z - move-result v0 - """.toInstructions() - val block3 = """ - invoke-virtual {p0}, ${descriptors[3]} - move-result-object p0 - return-object p0 - """.toInstructions() - - // create target instruction - val targetInstruction = BuilderInstruction11n(Opcode.CONST_4, 1, 0) - // and remaining instruction - val returnInstruction = BuilderInstruction11x(Opcode.RETURN_OBJECT, 1) - - // insert blocks and instructions - getTemplateNameImplementation.insertBlocks( - 0, - block1, - block2, - block3, - listOf( - targetInstruction, returnInstruction - ), - ) - - // create label for target instruction - val targetInstructionLabel = - getTemplateNameImplementation.newLabelForIndex(getTemplateNameImplementation.instructions.size - 2) - - // create branch instructions to the label - val ifEqzInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 1, targetInstructionLabel) - val ifNezInstruction = BuilderInstruction21t(Opcode.IF_NEZ, 0, targetInstructionLabel) - - // insert branch instructions - getTemplateNameImplementation.addInstruction( - block1.size, ifEqzInstruction - ) - getTemplateNameImplementation.addInstruction( - block1.size + block2.size + 1, ifNezInstruction - ) - - // create the method - return createMutableMethod( - this.type, - "getTemplateName", - "Ljava/lang/String;", - descriptors[0].split("->")[0], // a bit weird to get the type this way, - getTemplateNameImplementation - ) - } }