Merge pull request #2 from revanced/dalvik-patches

feat: migrate to Dalvik patches
This commit is contained in:
oSumAtrIX 2022-05-07 05:34:16 +02:00 committed by GitHub
commit 0f1a08ed84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 3698 additions and 297 deletions

5
.gitignore vendored
View File

@ -111,4 +111,7 @@ gradle-app.setting
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
# gradle/wrapper/gradle-wrapper.properties
# Potentially copyrighted test APK
*.apk

View File

@ -1,5 +1,5 @@
plugins {
kotlin("jvm") version "1.6.10"
kotlin("jvm") version "1.6.20"
java
`maven-publish`
}
@ -8,8 +8,9 @@ group = "app.revanced"
repositories {
mavenCentral()
mavenLocal()
maven {
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patcher") // note the "r"!
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") // note the "r"!
credentials {
// DO NOT set these variables in the project's gradle.properties.
// Instead, you should set them in:
@ -22,14 +23,10 @@ repositories {
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.10")
implementation(kotlin("stdlib"))
testImplementation(kotlin("test"))
implementation("org.ow2.asm:asm:9.2")
implementation("org.ow2.asm:asm-util:9.2")
implementation("org.ow2.asm:asm-tree:9.2")
implementation("org.ow2.asm:asm-commons:9.2")
implementation("app.revanced:revanced-patcher:1.+") // use latest version.
implementation("app.revanced:revanced-patcher:1.0.0-dev.9")
}
java {
@ -37,15 +34,21 @@ java {
withJavadocJar()
}
val isGitHubCI = System.getenv("GITHUB_ACTOR") != null
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patches") // note the "s"!
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
if (isGitHubCI) {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/revanced/revanced-patches") // note the "s"!
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
} else {
mavenLocal()
}
}
publications {
@ -53,4 +56,4 @@ publishing {
from(components["java"])
}
}
}
}

View File

@ -0,0 +1,14 @@
package app.revanced.extensions
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.builder.MutableMethodImplementation
internal fun MutableMethodImplementation.injectHideCall(
index: Int,
register: Int
) {
this.addInstruction(
index,
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->HideView(Landroid/view/View;)V".toInstruction()
)
}

View File

@ -1,12 +1,22 @@
package app.revanced.patches
import app.revanced.patcher.patch.Patch
import app.revanced.patches.ad.VideoAds
import app.revanced.patches.interaction.EnableSeekbarTapping
import app.revanced.patches.layout.*
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.patch.base.Patch
import app.revanced.patches.music.audio.CodecsUnlockPatch
import app.revanced.patches.music.audio.EnableAudioOnlyPatch
import app.revanced.patches.music.layout.RemoveTasteBuilderPatch
import app.revanced.patches.music.layout.RemoveUpgradeTabPatch
import app.revanced.patches.music.premium.BackgroundPlayPatch
import app.revanced.patches.youtube.ad.HomeAdsPatch
import app.revanced.patches.youtube.ad.HomePromoPatch
import app.revanced.patches.youtube.ad.VideoAdsPatch
import app.revanced.patches.youtube.interaction.EnableSeekbarTappingPatch
import app.revanced.patches.youtube.layout.*
import app.revanced.patches.youtube.misc.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.IntegrationsPatch
/**
* Index contains all the patches and should be imported when using this library.
* Index contains all the patches.
*/
@Suppress("Unused")
object Index {
@ -14,13 +24,22 @@ object Index {
* Array of patches.
* New patches should be added to the array.
*/
val patches: Array<() -> Patch> = arrayOf(
::VideoAds,
::MinimizedPlayback,
::CreateButtonRemover,
::HideReels,
::HideSuggestions,
::OldQualityLayout,
::EnableSeekbarTapping
val patches: List<() -> Patch<Data>> = listOf(
::IntegrationsPatch,
::FixLocaleConfigErrorPatch,
::HomeAdsPatch,
::VideoAdsPatch,
::HomePromoPatch,
::MinimizedPlaybackPatch,
::CreateButtonRemoverPatch,
::ShortsButtonRemoverPatch,
::HideReelsPatch,
::OldQualityLayoutPatch,
::EnableSeekbarTappingPatch,
::EnableAudioOnlyPatch,
::RemoveUpgradeTabPatch,
::RemoveTasteBuilderPatch,
::BackgroundPlayPatch,
::CodecsUnlockPatch
)
}
}

View File

@ -1,39 +0,0 @@
package app.revanced.patches.ad
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.signature.Signature
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.tree.MethodInsnNode
class VideoAds : Patch("VideoAds") {
override fun execute(cache: Cache): PatchResult {
val showVideoAdsMethodData = cache.methods["show-video-ads"].findParentMethod(
Signature(
"method",
Type.VOID_TYPE,
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
arrayOf(Type.BOOLEAN_TYPE),
null
)
) ?: return PatchResultError("Could not find required method to patch")
showVideoAdsMethodData.method.instructions.insertAt(
0,
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/vanced/libraries/youtube/whitelisting/Whitelist",
"shouldShowAds",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE)
)
)
return PatchResultSuccess()
}
}

View File

@ -1,49 +0,0 @@
package app.revanced.patches.interaction
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.*
class EnableSeekbarTapping : Patch("enable-seekbar-tapping") {
override fun execute(cache: Cache): PatchResult {
val patchData = cache.methods["enable-seekbar-tapping"]
val methodOPatchData = cache.methods["enable-seekbar-tapping-method-o"]
val methodPPatchData = cache.methods["enable-seekbar-tapping-method-p"]
val elseLabel = LabelNode()
patchData.method.instructions.insertAt(
patchData.scanData.endIndex,
InsnNode(Opcodes.ACONST_NULL),
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/razerman/youtube/preferences/BooleanPreferences",
"isTapSeekingEnabled",
"()Z"
),
JumpInsnNode(Opcodes.IFEQ, elseLabel),
VarInsnNode(Opcodes.ALOAD, 0),
VarInsnNode(Opcodes.ILOAD, 6),
MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
methodOPatchData.declaringClass.name,
methodOPatchData.method.name,
"(I)V"
),
VarInsnNode(Opcodes.ALOAD, 0),
VarInsnNode(Opcodes.ILOAD, 6),
MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
methodPPatchData.declaringClass.name,
methodPPatchData.method.name,
"(I)V"
),
elseLabel
)
return PatchResultSuccess()
}
}

View File

@ -1,32 +0,0 @@
package app.revanced.patches.layout
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.VarInsnNode
class CreateButtonRemover : Patch("create-button-remover") {
override fun execute(cache: Cache): PatchResult {
val patchData = cache.methods["create-button-patch"]
patchData.method.instructions.insertAt(
patchData.scanData.endIndex - 1,
VarInsnNode(
Opcodes.ALOAD,
6
),
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/razerman/youtube/XAdRemover",
"hideCreateButton",
"(Landroid/view/View;)V"
)
)
return PatchResultSuccess()
}
}

View File

@ -1,29 +0,0 @@
package app.revanced.patches.layout
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.VarInsnNode
class HideReels : Patch("hide-reels") {
override fun execute(cache: Cache): PatchResult {
val patchData = cache.methods["hide-reel-patch"]
patchData.method.instructions.insertAt(
patchData.scanData.endIndex + 1,
VarInsnNode(Opcodes.ALOAD, 18),
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/razerman/youtube/XAdRemover",
"HideReels",
"(Landroid/view/View;)V"
)
)
return PatchResultSuccess()
}
}

View File

@ -1,52 +0,0 @@
package app.revanced.patches.layout
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.signature.Signature
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.VarInsnNode
class HideSuggestions : Patch("hide-suggestions") {
override fun execute(cache: Cache): PatchResult {
val method = cache.methods["hide-suggestions-patch"].findParentMethod(
Signature(
"hide-suggestions-method",
Type.VOID_TYPE,
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
arrayOf(Type.BOOLEAN_TYPE),
arrayOf(
Opcodes.ALOAD,
Opcodes.ILOAD,
Opcodes.PUTFIELD,
Opcodes.ALOAD,
Opcodes.GETFIELD
)
)
) ?: return PatchResultError("Parent method hide-suggestions-method has not been found")
method.method.instructions.insertAt(
0,
VarInsnNode(Opcodes.ILOAD, 1),
MethodInsnNode(
Opcodes.INVOKESTATIC,
"java/lang/Boolean",
"valueOf",
"(Z)Ljava/lang/Boolean"
),
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/razerman/youtube/XAdRemover",
"HideReels",
"(Landroid/view/View;)V"
)
)
return PatchResultSuccess()
}
}

View File

@ -1,13 +0,0 @@
package app.revanced.patches.layout
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
class MinimizedPlayback : Patch("minimized-playback") {
override fun execute(cache: Cache): PatchResult {
cache.methods["minimized-playback-manager"].method.instructions.clear()
return PatchResultSuccess()
}
}

View File

@ -1,52 +0,0 @@
package app.revanced.patches.layout
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.signature.Signature
import app.revanced.patcher.util.ExtraTypes
import app.revanced.patcher.writer.ASMWriter.insertAt
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.JumpInsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.VarInsnNode
class OldQualityLayout : Patch("old-quality-restore") {
override fun execute(cache: Cache): PatchResult {
val method = cache.methods["old-quality-patch"].findParentMethod(
Signature(
"old-quality-patch-method",
ExtraTypes.Any,
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
arrayOf(),
arrayOf(
Opcodes.ALOAD,
Opcodes.GETFIELD,
Opcodes.ISTORE,
Opcodes.ICONST_3,
Opcodes.ISTORE
)
)
) ?: return PatchResultError("Parent method old-quality-patch-method has not been found")
method.method.instructions.insertAt(
0,
MethodInsnNode(
Opcodes.INVOKESTATIC,
"fi/razerman/youtube/XGlobals",
"useOldStyleQualitySettings",
"()Z"
),
VarInsnNode(Opcodes.ISTORE, 1),
VarInsnNode(Opcodes.ILOAD, 1),
JumpInsnNode(
Opcodes.IFNE,
(method.method.instructions[method.scanData.endIndex + 3] as JumpInsnNode).label
),
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,157 @@
package app.revanced.patches.music.audio
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.data.implementation.toMethodWalker
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val packageMetadata = listOf(
PackageMetadata(
"com.google.android.apps.youtube.music",
listOf("5.03.50")
)
)
private val patchMetadata = PatchMetadata(
"codecs-unlock",
"Audio codecs unlock patch",
"Enables more audio codecs. Usually results in better audio quality but may depend on song and device.",
packageMetadata,
"0.0.1"
)
class CodecsUnlockPatch : BytecodePatch(
patchMetadata,
listOf(
MethodSignature(
MethodSignatureMetadata(
"codec-lock-method",
MethodMetadata(
"Labwj;",
"a",
),
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
packageMetadata,
"Required signature for ${patchMetadata.name}. Discovered in version 5.03.50.",
"0.0.1"
),
"L",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "L", "L", "L"),
listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.SGET,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_DIRECT,
Opcode.RETURN_OBJECT
)
),
MethodSignature(
MethodSignatureMetadata(
"all-codecs-reference-method",
MethodMetadata(
"Laari;",
"b",
),
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
packageMetadata,
"Required signature for ${patchMetadata.name}. Discovered in version 5.03.50.",
"0.0.1"
),
"J",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("L"),
listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.IPUT_BOOLEAN,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.IPUT_BOOLEAN,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.MOVE_EXCEPTION,
Opcode.INVOKE_SUPER,
Opcode.MOVE_RESULT_WIDE,
Opcode.RETURN_WIDE
),
listOf("itag")
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
var result = signatures.first().result!!
val implementation = result.method.implementation!!
val instructionIndex = result.scanData.startIndex
result = signatures.last().result!!
val codecMethod = data
.toMethodWalker(result.immutableMethod)
.walk(result.scanData.startIndex)
.getMethod()
implementation.replaceInstruction(
instructionIndex,
"invoke-static {}, ${codecMethod.definingClass}->${codecMethod.name}()Ljava/util/Set;".toInstruction()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,133 @@
package app.revanced.patches.music.audio
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.apps.youtube.music",
listOf("5.03.50")
)
)
class EnableAudioOnlyPatch : BytecodePatch(
PatchMetadata(
"audio-only-playback-patch",
"Audio Only Mode Patch",
"Add the option to play music without video.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"audio-only-method-signature",
MethodMetadata("Lgmd;", "c"),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("L", "Z"),
listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQ,
Opcode.CONST_4,
Opcode.GOTO,
Opcode.NOP,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IF_EQZ,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.RETURN_VOID
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!.findParentMethod(
MethodSignature(
MethodSignatureMetadata(
"audio-only-enabler-method",
MethodMetadata("Lgmd;", "d"),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf(),
listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.GOTO,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.RETURN
)
)
) ?: return PatchResultError("Required method for ${metadata.shortName} not found.")
val implementation = result.method.implementation!!
implementation.replaceInstruction(
implementation.instructions.count() - 1,
"const/4 v0, 0x1".toInstruction()
)
implementation.addInstruction(
"return v0".toInstruction()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,94 @@
package app.revanced.patches.music.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.apps.youtube.music",
listOf("5.03.50")
)
)
class RemoveTasteBuilderPatch : BytecodePatch(
PatchMetadata(
"tasteBuilder-remover",
"Remove TasteBuilder Patch",
"Removes the \"Tell us which artists you like\" card from the Home screen. The same functionality can be triggered from the settings anyway.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"taste-builder-constructor",
MethodMetadata("Lkyu;", "<init>"),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Required signature for this patch.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("L", "L", "L", "L"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!
val implementation = result.method.implementation!!
val insertIndex = result.scanData.endIndex - 8
val register = (implementation.instructions[insertIndex] as Instruction22c).registerA
val instructionList =
"""
const/16 v1, 0x8
invoke-virtual {v${register}, v1}, Landroid/view/View;->setVisibility(I)V
""".trimIndent().toInstructions().toMutableList()
implementation.addInstructions(
insertIndex,
instructionList
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,151 @@
package app.revanced.patches.music.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.apps.youtube.music",
listOf("5.03.50")
)
)
class RemoveUpgradeTabPatch : BytecodePatch(
PatchMetadata(
"upgrade-tab-remover",
"Remove Upgrade Tab Patch",
"Remove the upgrade tab from t he pivot bar in YouTube music.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"pivot-bar-constructor",
MethodMetadata("Lhfu;", "<init2>"), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Required signature for this patch.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("L", "Z"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.CONST_4,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.GOTO,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.GOTO,
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.NOP,
Opcode.IPUT_OBJECT,
Opcode.RETURN_VOID
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!
val implementation = result.method.implementation!!
val pivotBarElementFieldRef =
(implementation.instructions[result.scanData.endIndex - 1] as Instruction22c).reference
val register = (implementation.instructions.first() as Instruction35c).registerC
// first compile all the needed instructions
val instructionList =
"""
invoke-interface { v0 }, Ljava/util/List;->size()I
move-result v1
const/4 v2, 0x3
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
iput-object v0, v$register, $pivotBarElementFieldRef
""".trimIndent().toInstructions().toMutableList()
// replace the instruction to retain the label at given index
implementation.replaceInstruction(
result.scanData.endIndex - 1,
instructionList[0] // invoke-interface
)
// do not forget to remove this instruction since we added it already
instructionList.removeFirst()
val exitInstruction = instructionList.last() // iput-object
implementation.addInstruction(
result.scanData.endIndex,
exitInstruction
)
// do not forget to remove this instruction since we added it already
instructionList.removeLast()
// add the necessary if statement to remove the upgrade tab button in case it exists
instructionList.add(
2, // if-le
BuilderInstruction22t(
Opcode.IF_LE,
1, 2,
implementation.newLabelForIndex(result.scanData.endIndex)
)
)
implementation.addInstructions(
result.scanData.endIndex,
instructionList
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,92 @@
package app.revanced.patches.music.premium
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.apps.youtube.music",
listOf("5.03.50")
)
)
class BackgroundPlayPatch : BytecodePatch(
PatchMetadata(
"background-play",
"Enable Background Playback Patch",
"Enable playing music in the background.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"background-playback-disabler-method",
MethodMetadata("Lafgf;", "e"),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"Z",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L"),
listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET,
Opcode.AND_INT_LIT16,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.GOTO,
Opcode.SGET_OBJECT,
Opcode.GOTO,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ,
Opcode.CONST_4,
Opcode.RETURN,
Opcode.RETURN,
Opcode.RETURN
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
signatures.first().result!!.method.implementation!!.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""".trimIndent().toInstructions()
)
return PatchResultSuccess()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,188 @@
package app.revanced.patches.youtube.ad
import app.revanced.extensions.injectHideCall
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.data.implementation.toMethodWalker
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.03.38", "17.14.35")
)
)
private val patchMetadata = PatchMetadata(
"home-promo-ads",
"Home Promo Ads Patch",
"Patch to remove promoted ads in YouTube",
compatiblePackages,
"0.0.1"
)
private val signatureDescription = "Required signature for ${patchMetadata.name}. Discovered in version 17.03.38."
class HomePromoPatch : BytecodePatch(
patchMetadata,
listOf(
MethodSignature(
MethodSignatureMetadata(
"promoted-discovery-app-parent-method",
MethodMetadata(
"Ljjl;",
"lG",
),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
signatureDescription,
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
listOf("L", "L"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.IGET_BOOLEAN,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.NEW_ARRAY,
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_GE,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.APUT_OBJECT,
Opcode.ADD_INT_LIT8,
Opcode.GOTO
)
),
MethodSignature(
MethodSignatureMetadata(
"promoted-discovery-action-parent-method",
MethodMetadata(
"Ljjc;",
"lG",
),
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
signatureDescription,
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
listOf("L", "L"),
listOf(
Opcode.MOVE_OBJECT_FROM16,
Opcode.MOVE_OBJECT_FROM16,
Opcode.MOVE_OBJECT_FROM16,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL_RANGE,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.IGET_BOOLEAN,
Opcode.CONST_4,
Opcode.XOR_INT_2ADDR,
Opcode.IGET_BOOLEAN,
Opcode.INVOKE_DIRECT,
Opcode.IGET_BOOLEAN,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CONST_4,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
for (signature in signatures) {
val result = signature.result!!
val methodMetadata = MethodMetadata(signature.metadata.methodMetadata!!.definingClass, "d")
val requiredMethod = result.findParentMethod(
MethodSignature(
MethodSignatureMetadata(
"promoted-discovery-action-parent-method",
methodMetadata,
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
signatureDescription,
"0.0.1"
),
"V",
AccessFlags.PRIVATE or AccessFlags.FINAL,
listOf("Z", "Z"),
null
)
)
?: return PatchResultError("Required parent method ${methodMetadata.name} could not be found in ${methodMetadata.definingClass}")
val toBePatchedInvokeOffset =
requiredMethod.immutableMethod.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_DIRECT }
val toBePatchedMethod = data
.toMethodWalker(requiredMethod.immutableMethod)
.walk(toBePatchedInvokeOffset, true)
.getMethod() as MutableMethod
val implementation = toBePatchedMethod.implementation!!
val invokeVirtualOffset = implementation.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_VIRTUAL }
val moveResultInstruction = implementation.instructions[invokeVirtualOffset + 1]
if (moveResultInstruction.opcode != Opcode.MOVE_RESULT_OBJECT)
return PatchResultError("The toBePatchedInvokeOffset offset was wrong in ${metadata.name}")
val register = (moveResultInstruction as Instruction11x).registerA
implementation.injectHideCall(invokeVirtualOffset + 2, register)
}
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,112 @@
package app.revanced.patches.youtube.ad
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val packageMetadata = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
private val patchMetadata = PatchMetadata(
"video-ads",
"YouTube Video Ads Patch",
"Patch to remove ads in the YouTube video player.",
packageMetadata,
"0.0.1"
)
class VideoAdsPatch : BytecodePatch(
patchMetadata,
listOf(
MethodSignature(
MethodSignatureMetadata(
"show-video-ads-constructor",
MethodMetadata(
"zai",
"<init>",
),
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
packageMetadata,
"Required signature for ${patchMetadata.name}. Discovered in version 17.14.35.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("L", "L", "L"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
null, // either CONST_4 or CONST_16
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
var result = signatures.first().result!!
val responsibleMethodSignature = MethodSignature(
MethodSignatureMetadata(
"show-video-ads-method",
MethodMetadata(
"zai",
null // unknown
),
PatternScanMethod.Direct(),
packageMetadata,
"Signature to find the method, which is responsible for showing the video ads. Discovered in version 17.14.35",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("Z"),
null
)
result = result.findParentMethod(
responsibleMethodSignature
) ?: return PatchResultError(
"Could not find parent method with signature ${responsibleMethodSignature.metadata.name}"
)
// Override the parameter by calling shouldShowAds and setting the parameter to the result
result.method.implementation!!.addInstructions(
0,
"""
invoke-static { }, Lfi/vanced/libraries/youtube/whitelisting/Whitelist;->shouldShowAds()Z
move-result v1
""".trimIndent().toInstructions()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,196 @@
package app.revanced.patches.youtube.interaction
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.formats.Instruction11n
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class EnableSeekbarTappingPatch : BytecodePatch(
PatchMetadata(
"seekbar-tapping",
"Enable seekbar tapping patch",
"Enable tapping on the seekbar of the YouTube player.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"enable-seekbar-tapping-parent-signature",
MethodMetadata("Lesa;", "<init>"), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for a parent method, which is needed to find the actual method required to be patched.",
"0.0.1"
),
"L",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf(),
listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.NEW_ARRAY,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.APUT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.APUT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT
)
),
MethodSignature(
MethodSignatureMetadata(
"enable-seekbar-tapping-signature",
MethodMetadata("Lesa;", "onTouchEvent"), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("L"),
listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INT_TO_FLOAT,
Opcode.IGET,
Opcode.IGET_OBJECT,
Opcode.IGET,
Opcode.DIV_INT_2ADDR,
Opcode.ADD_INT,
Opcode.SUB_INT_2ADDR,
Opcode.INT_TO_FLOAT,
Opcode.CMPG_FLOAT,
Opcode.IF_GTZ,
Opcode.INT_TO_FLOAT,
Opcode.CMPG_FLOAT,
Opcode.IF_GTZ,
Opcode.CONST_4,
Opcode.INVOKE_INTERFACE,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.FLOAT_TO_INT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.FLOAT_TO_INT,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_VIRTUAL
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
var result = signatures.first().result!!
val tapSeekMethods = mutableMapOf<String, Method>()
// find the methods which tap the seekbar
for (it in result.definingClassProxy.immutableClass.methods) {
if (it.implementation == null) continue
val instructions = it.implementation!!.instructions
// here we make sure we actually find the method because it has more then 7 instructions
if (instructions.count() < 7) continue
// we know that the 7th instruction has the opcode CONST_4
val instruction = instructions.elementAt(6)
if (instruction.opcode != Opcode.CONST_4) continue
// the literal for this instruction has to be either 1 or 2
val literal = (instruction as Instruction11n).narrowLiteral
// method founds
if (literal == 1) tapSeekMethods["P"] = it
if (literal == 2) tapSeekMethods["O"] = it
}
// replace map because we dont need the upper one anymore
result = signatures.last().result!!
val implementation = result.method.implementation!!
// if tap-seeking is enabled, do not invoke the two methods below
val pMethod = tapSeekMethods["P"]!!
val oMethod = tapSeekMethods["O"]!!
// get the required register
val instruction = implementation.instructions[result.scanData.endIndex]
if (instruction.opcode != Opcode.INVOKE_VIRTUAL)
return PatchResultError("Could not find the correct register")
val register = (instruction as Instruction35c).registerC
// the instructions are written in reverse order.
implementation.addInstructions(
result.scanData.endIndex + 1,
"""
invoke-virtual { v$register, v2 }, ${oMethod.definingClass}->${oMethod.name}(I)V
invoke-virtual { v$register, v2 }, ${pMethod.definingClass}->${pMethod.name}(I)V
""".trimIndent().toInstructions()
)
// if tap-seeking is disabled, do not invoke the two methods above by jumping to the else label
val elseLabel = implementation.newLabelForIndex(result.scanData.endIndex + 1)
implementation.addInstruction(
result.scanData.endIndex + 1,
BuilderInstruction21t(Opcode.IF_EQZ, 0, elseLabel)
)
implementation.addInstructions(
result.scanData.endIndex + 1,
"""
invoke-static { }, Lfi/razerman/youtube/preferences/BooleanPreferences;->isTapSeekingEnabled()Z
move-result v0
""".trimIndent().toInstructions()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,102 @@
package app.revanced.patches.youtube.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class CreateButtonRemoverPatch : BytecodePatch(
PatchMetadata(
"create-button",
"Create button patch",
"Disable the create button.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"create-button-method",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("Z"),
listOf(
Opcode.IGET,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.CONST,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.INVOKE_DIRECT_RANGE,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!
// Get the required register which holds the view object we need to pass to the method hideCreateButton
val implementation = result.method.implementation!!
val instruction = implementation.instructions[result.scanData.endIndex + 1]
if (instruction.opcode != Opcode.INVOKE_STATIC)
return PatchResultError("Could not find the correct register")
val register = (instruction as Instruction35c).registerC
// Hide the button view via proxy by passing it to the hideCreateButton method
implementation.addInstruction(
result.scanData.endIndex + 1,
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->hideCreateButton(Landroid/view/View;)V".toInstruction()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,116 @@
package app.revanced.patches.youtube.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class HideReelsPatch : BytecodePatch(
PatchMetadata(
"hide-reels",
"Hide reels patch",
"Hide reels on the page.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"hide-reels-signature",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(3), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf(
"L",
"L",
"L",
"L",
"L",
"L",
"L",
"L",
"L",
"L",
"L",
"[B",
"[B",
"[B",
"[B",
"[B",
"[B",
"[B"
),
listOf(
Opcode.MOVE_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.INVOKE_DIRECT,
Opcode.MOVE_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT_FROM16,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT_FROM16,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.MOVE_OBJECT_FROM16,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IPUT_OBJECT
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!
val implementation = result.method.implementation!!
// HideReel will hide the reel view before it is being used,
// so we pass the view to the HideReel method
implementation.addInstruction(
result.scanData.endIndex,
"invoke-static { v2 }, Lfi/razerman/youtube/XAdRemover;->HideReel(Landroid/view/View;)V".toInstruction()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,89 @@
package app.revanced.patches.youtube.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class MinimizedPlaybackPatch : BytecodePatch(
PatchMetadata(
"minimized-playback",
"Minimized Playback Patch",
"Enable minimized and background playback.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"minimized-playback-manager",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the method required to be patched.",
"0.0.1"
),
"Z",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L"),
listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET,
Opcode.AND_INT_LIT16,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.CONST,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.GOTO,
Opcode.SGET_OBJECT,
Opcode.GOTO,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
// Instead of removing all instructions like Vanced,
// we return the method at the beginning instead
signatures.first().result!!.method.implementation!!.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""".trimIndent().toInstructions()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,127 @@
package app.revanced.patches.youtube.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultError
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class OldQualityLayoutPatch : BytecodePatch(
PatchMetadata(
"old-quality-layout",
"Old Quality Layout Patch",
"Enable the original quality flyout menu",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"old-quality-parent-method-signature",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature to find a parent method required by the Old Quality Layout patch.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
listOf("L", "L", "L", "L", "L", "L", "L", "[B"),
listOf(
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET_BOOLEAN,
Opcode.CONST_4,
Opcode.CONST_4,
Opcode.CONST_4,
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
var result = signatures.first().result!!
result = result.findParentMethod(
MethodSignature(
MethodSignatureMetadata(
"old-quality-method-signature",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature to find the method required by the Old Quality Layout patch",
"0.0.1"
),
"L",
AccessFlags.FINAL or AccessFlags.PRIVATE,
listOf("Z"),
listOf(
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.GOTO,
Opcode.IGET_OBJECT,
)
)
) ?: return PatchResultError("Method old-quality-patch-method has not been found")
val implementation = result.method.implementation!!
// if useOldStyleQualitySettings == true, jump over all instructions
val jmpInstruction =
BuilderInstruction21t(
Opcode.IF_NEZ,
0,
implementation.instructions[result.scanData.endIndex].location.labels.first()
)
implementation.addInstruction(5, jmpInstruction)
implementation.addInstructions(
0,
"""
invoke-static { }, Lfi/razerman/youtube/XGlobals;->useOldStyleQualitySettings()Z
move-result v0
""".trimIndent().toInstructions()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,135 @@
package app.revanced.patches.youtube.layout
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class ShortsButtonRemoverPatch : BytecodePatch(
PatchMetadata(
"shorts-button",
"Shorts button patch",
"Hide the shorts button.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"pivotbar-buttons-method-tabenum",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the pivotbar method that creates all button views.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("Z"),
listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.INVOKE_STATIC, // SomeEnum.fromValue(tabOrdinal)
Opcode.MOVE_RESULT_OBJECT
)
),
MethodSignature(
MethodSignatureMetadata(
"pivotbar-buttons-method-view",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Signature for the pivotbar method that creates all button views.",
"0.0.1"
),
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("Z"),
listOf(
Opcode.NEW_INSTANCE, // new StateListDrawable()
Opcode.INVOKE_DIRECT,
Opcode.NEW_ARRAY,
Opcode.CONST,
Opcode.CONST_16,
Opcode.APUT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.MOVE,
Opcode.MOVE_OBJECT,
Opcode.INVOKE_VIRTUAL_RANGE, // pivotBar.getView(drawable, tabName, z, i, map, akebVar, optional)
Opcode.MOVE_RESULT_OBJECT,
)
),
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result1 = signatures.first().result!!
val implementation1 = result1.method.implementation!!
val moveEnumInstruction = implementation1.instructions[result1.scanData.endIndex]
val enumRegister = (moveEnumInstruction as Instruction11x).registerA
val result2 = signatures.last().result!!
val implementation2 = result2.method.implementation!!
val moveViewInstruction = implementation2.instructions[result2.scanData.endIndex]
val viewRegister = (moveViewInstruction as Instruction11x).registerA
// Save the tab enum in XGlobals to avoid smali/register workarounds
implementation1.addInstruction(
result1.scanData.endIndex + 1,
"sput-object v$enumRegister, Lfi/razerman/youtube/XGlobals;->lastPivotTab:Ljava/lang/Enum;".toInstruction()
)
// Hide the button view via proxy by passing it to the hideShortsButton method
// It only hides it if the last tab name is "TAB_SHORTS"
implementation2.addInstruction(
result2.scanData.endIndex + 2,
"invoke-static { v$viewRegister }, Lfi/razerman/youtube/XAdRemover;->hideShortsButton(Landroid/view/View;)V".toInstruction()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,46 @@
package app.revanced.patches.youtube.misc
import app.revanced.patcher.data.implementation.ResourceData
import app.revanced.patcher.patch.implementation.ResourcePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import org.w3c.dom.Element
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.14.35")
)
)
class FixLocaleConfigErrorPatch : ResourcePatch(
PatchMetadata(
"locale-config-fix",
"Manifest attribute fix patch",
"Fix an error when building the resources by patching the manifest file.",
compatiblePackages,
"0.0.1"
),
) {
override fun execute(data: ResourceData): PatchResult {
// create an xml editor instance
val editor = data.getXmlEditor("AndroidManifest.xml")
// edit the application nodes attribute...
val applicationNode = editor
.file
.getElementsByTagName("application")
.item(0) as Element
// by replacing the attributes name
val attribute = "android:localeConfig"
applicationNode.setAttribute("localeConfig", applicationNode.getAttribute(attribute))
applicationNode.removeAttribute("android:localeConfig")
// close & save the modified file
editor.close()
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,125 @@
package app.revanced.patches.youtube.misc
import app.revanced.patcher.data.implementation.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.implementation.BytecodePatch
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
import app.revanced.patcher.patch.implementation.misc.PatchResult
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.MethodSignatureMetadata
import app.revanced.patcher.signature.PatternScanMethod
import app.revanced.patcher.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.immutable.ImmutableMethod
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
private val compatiblePackages = listOf(
PackageMetadata(
"com.google.android.youtube",
listOf("17.03.38", "17.14.35")
)
)
class IntegrationsPatch : BytecodePatch(
PatchMetadata(
"integrations",
"Inject Integrations Patch",
"Applies mandatory patches to implement the ReVanced integrations into the application.",
compatiblePackages,
"0.0.1"
),
listOf(
MethodSignature(
MethodSignatureMetadata(
"integrations-patch",
MethodMetadata(null, null), // unknown
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
compatiblePackages,
"Inject the integrations into the application with the method of this signature",
"0.0.1"
),
"V",
AccessFlags.PUBLIC.value,
listOf(),
listOf(
Opcode.SGET_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT,
Opcode.CONST_STRING,
Opcode.IF_NEZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.MOVE_OBJECT,
Opcode.CHECK_CAST,
Opcode.MOVE_OBJECT,
Opcode.CHECK_CAST,
Opcode.CONST_4,
Opcode.CONST_STRING,
Opcode.INVOKE_INTERFACE_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.SPUT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL,
Opcode.INVOKE_SUPER,
Opcode.INVOKE_VIRTUAL
)
)
)
) {
override fun execute(data: BytecodeData): PatchResult {
val result = signatures.first().result!!
val implementation = result.method.implementation!!
val count = implementation.registerCount - 1
implementation.addInstructions(
result.scanData.endIndex + 1,
"""
invoke-static {v$count}, Lpl/jakubweg/StringRef;->setContext(Landroid/content/Context;)V
sput-object v$count, Lapp/revanced/integrations/Globals;->context:Landroid/content/Context;
""".trimIndent().toInstructions()
)
val classDef = result.definingClassProxy.resolve()
classDef.methods.add(
ImmutableMethod(
classDef.type,
"getAppContext",
null,
"Landroid/content/Context;",
AccessFlags.PUBLIC or AccessFlags.STATIC,
null,
null,
ImmutableMethodImplementation(
1,
"""
invoke-static { }, Lapp/revanced/integrations/Globals;->getAppContext()Landroid/content/Context;
move-result-object v0
return-object v0
""".trimIndent().toInstructions(),
null,
null
)
).toMutable()
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,59 @@
package app.revanced.patches
import app.revanced.patcher.Patcher
import app.revanced.patcher.signature.MethodMetadata
import app.revanced.patcher.signature.MethodSignature
import app.revanced.patcher.signature.PatternScanMethod
import org.jf.dexlib2.iface.Method
import org.junit.Test
import java.io.File
internal class SignatureChecker {
@Test
fun checkSignatures() {
return
// FIXME: instead of having this as a test, it should be turned into a task which can be ran manually
val file = File("stock.apk")
if (!file.exists()) {
throw IllegalStateException("Missing $file! To run this test, please place stock.apk here: ${file.absolutePath}")
}
val patcher = Patcher(file, "signatureCheckerCache", false)
patcher.addPatches(Index.patches.map { it() })
val unresolved = mutableListOf<MethodSignature>()
for (signature in patcher.resolveSignatures()) {
if (!signature.resolved) {
unresolved.add(signature)
continue
}
val patternScanMethod = signature.metadata.patternScanMethod
if (patternScanMethod is PatternScanMethod.Fuzzy) {
val warnings = patternScanMethod.warnings!!
val method = signature.result!!.method
val methodFromMetadata =
if (signature.metadata.methodMetadata != null) signature.metadata.methodMetadata!! else MethodMetadata(
null,
null
)
println("Signature: ${signature.metadata.name}.\nMethod: ${methodFromMetadata.definingClass}->${methodFromMetadata.name} (Signature matches: ${method.definingClass}->${method.toStr()})\nWarnings: ${warnings.count()}")
for (warning in warnings) {
println("${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})")
}
println("=".repeat(20))
}
}
if (unresolved.isNotEmpty()) {
val base = Exception("${unresolved.size} signatures were not resolved.")
for (signature in unresolved) {
base.addSuppressed(Exception("Signature ${signature.metadata.name} was not resolved!"))
}
throw base
}
}
private fun Method.toStr(): String {
return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}"
}
}