diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/MediaFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/MediaFingerprint.kt new file mode 100644 index 000000000..bfc52935b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/MediaFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint + +object MediaFingerprint : MethodFingerprint( + strings = listOf("is_paid_partnership", "story_ad_headline", "is_panorama") +) diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ShowAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ShowAdFingerprint.kt new file mode 100644 index 000000000..68edc8c92 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ShowAdFingerprint.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object ShowAdFingerprint : MethodFingerprint( + "Z", + AccessFlags.PUBLIC or AccessFlags.STATIC or AccessFlags.FINAL, + listOf("L", "L", "Z", "Z"), + opcodes = listOf( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.RETURN, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.RETURN, + ), +) diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/GenericMediaAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/GenericMediaAdFingerprint.kt new file mode 100644 index 000000000..f4988a135 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/GenericMediaAdFingerprint.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads + +import org.jf.dexlib2.Opcode + +object GenericMediaAdFingerprint : MediaAdFingerprint( + opcodes = listOf( + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.CONST_4, + Opcode.RETURN, + ) +) { + override fun toString() = result!!.method.toString() +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/MediaAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/MediaAdFingerprint.kt new file mode 100644 index 000000000..d505347af --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/MediaAdFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode +import org.jf.dexlib2.iface.Method + +abstract class MediaAdFingerprint( + returnType: String? = "Z", + access: Int? = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters: Iterable? = listOf(), + opcodes: Iterable?, + customFingerprint: ((methodDef: Method) -> Boolean)? = null +) : MethodFingerprint( + returnType, + access, + parameters, + opcodes, + customFingerprint = customFingerprint +) { + abstract override fun toString(): String +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/PaidPartnershipAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/PaidPartnershipAdFingerprint.kt new file mode 100644 index 000000000..0dfa62a77 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/PaidPartnershipAdFingerprint.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads + +import org.jf.dexlib2.Opcode +import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.reference.MethodReference + +object PaidPartnershipAdFingerprint : MediaAdFingerprint( + "V", + null, + listOf("L", "L"), + listOf( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IPUT_BOOLEAN, + Opcode.IPUT_BOOLEAN + ), + customFingerprint = { methodDef -> + methodDef.definingClass.endsWith("ClipsEditMetadataController;") + } +) { + override fun toString() = result!!.let { + val adCheckIndex = it.scanResult.patternScanResult!!.startIndex + val adCheckInstruction = it.method.implementation!!.instructions.elementAt(adCheckIndex) + + val adCheckMethod = (adCheckInstruction as ReferenceInstruction).reference as MethodReference + + adCheckMethod.toString() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/ShoppingAdFingerprint.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/ShoppingAdFingerprint.kt new file mode 100644 index 000000000..28bd08a2e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ads/ShoppingAdFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads + +import org.jf.dexlib2.Opcode + +object ShoppingAdFingerprint : MediaAdFingerprint( + opcodes = listOf( + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.XOR_INT_LIT8, + Opcode.IF_EQZ, + ) +) { + override fun toString() = result!!.method.toString() +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/patch/HideTimelineAdsPatch.kt b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/patch/HideTimelineAdsPatch.kt new file mode 100644 index 000000000..5eb220cfc --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/patch/HideTimelineAdsPatch.kt @@ -0,0 +1,108 @@ +package app.revanced.patches.instagram.patches.ads.timeline.patch + +import app.revanced.extensions.toErrorResult +import app.revanced.patcher.annotation.* +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.extensions.removeInstruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.MediaFingerprint +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ShowAdFingerprint +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads.GenericMediaAdFingerprint +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads.MediaAdFingerprint +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads.PaidPartnershipAdFingerprint +import app.revanced.patches.instagram.patches.ads.timeline.fingerprints.ads.ShoppingAdFingerprint +import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction + +@Patch +@Name("hide-timeline-ads") +@Description("Removes ads from the timeline.") +@Compatibility([Package("com.instagram.android")]) +@Version("0.0.1") +class HideTimelineAdsPatch : BytecodePatch( + listOf( + ShowAdFingerprint, + MediaFingerprint, + PaidPartnershipAdFingerprint // Unlike the other ads this one is resolved from all classes. + ) +) { + override fun execute(context: BytecodeContext): PatchResult { + // region Resolve required methods to check for ads. + + ShowAdFingerprint.result ?: return ShowAdFingerprint.toErrorResult() + + PaidPartnershipAdFingerprint.result ?: return PaidPartnershipAdFingerprint.toErrorResult() + + MediaFingerprint.result?.let { + GenericMediaAdFingerprint.resolve(context, it.classDef) + ShoppingAdFingerprint.resolve(context, it.classDef) + + return@let + } ?: return MediaFingerprint.toErrorResult() + + // endregion + + ShowAdFingerprint.result!!.apply { + // region Create instructions. + + val scanStart = scanResult.patternScanResult!!.startIndex + val jumpIndex = scanStart - 1 + + val mediaInstanceRegister = (mutableMethod.instruction(scanStart) as FiveRegisterInstruction).registerC + val freeRegister = (mutableMethod.instruction(jumpIndex) as OneRegisterInstruction).registerA + + val returnFalseLabel = "an_ad" + + val checkForAdInstructions = + listOf(GenericMediaAdFingerprint, PaidPartnershipAdFingerprint, ShoppingAdFingerprint) + .map(MediaAdFingerprint::toString) + .joinToString("\n") { + """ + invoke-virtual {v$mediaInstanceRegister}, $it + move-result v$freeRegister + if-nez v$freeRegister, :$returnFalseLabel + """.trimIndent() + }.let { "$it\nconst/4 v0, 0x1\nreturn v0" } + + // endregion + + // region Patch. + + val insertIndex = scanStart + 3 + + mutableMethod.addInstructions( + insertIndex, + checkForAdInstructions, + listOf( + ExternalLabel( + returnFalseLabel, + mutableMethod.instruction(mutableMethod.implementation!!.instructions.size - 2 /* return false = ad */) + ) + ) + ) + + // endregion + + // region Jump to checks for ads from previous patch. + + mutableMethod.apply { + addInstructions( + jumpIndex + 1, + "if-nez v$freeRegister, :start_check", + listOf(ExternalLabel("start_check", instruction(insertIndex))) + ) + }.removeInstruction(jumpIndex) + + // endregion + } + + return PatchResultSuccess() + } +} \ No newline at end of file