mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-13 21:07:17 +02:00
chore(LithoFilterPatch): add compatibility with older versions of AGP
This commit is contained in:
parent
cc71acf44e
commit
435154cae3
@ -5,117 +5,101 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH
|
import app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH
|
||||||
|
import app.revanced.patches.shared.litho.fingerprints.ByteBufferFingerprint
|
||||||
import app.revanced.patches.shared.litho.fingerprints.EmptyComponentsFingerprint
|
import app.revanced.patches.shared.litho.fingerprints.EmptyComponentsFingerprint
|
||||||
import app.revanced.patches.shared.litho.fingerprints.LithoFilterPatchConstructorFingerprint
|
|
||||||
import app.revanced.patches.shared.litho.fingerprints.PathBuilderFingerprint
|
import app.revanced.patches.shared.litho.fingerprints.PathBuilderFingerprint
|
||||||
import app.revanced.patches.shared.litho.fingerprints.SetByteBufferFingerprint
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.getStringInstructionIndex
|
import app.revanced.util.getStringInstructionIndex
|
||||||
import app.revanced.util.getTargetIndexOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.getTargetIndexReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import app.revanced.util.getTargetIndexWithFieldReferenceTypeOrThrow
|
|
||||||
import app.revanced.util.resultOrThrow
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection", "unused")
|
@Suppress("SpellCheckingInspection", "unused")
|
||||||
object LithoFilterPatch : BytecodePatch(
|
object LithoFilterPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
|
ByteBufferFingerprint,
|
||||||
EmptyComponentsFingerprint,
|
EmptyComponentsFingerprint,
|
||||||
LithoFilterPatchConstructorFingerprint,
|
|
||||||
SetByteBufferFingerprint
|
|
||||||
)
|
)
|
||||||
), Closeable {
|
), Closeable {
|
||||||
private const val INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR =
|
||||||
"$COMPONENTS_PATH/LithoFilterPatch;"
|
"$COMPONENTS_PATH/LithoFilterPatch;"
|
||||||
|
|
||||||
private const val INTEGRATIONS_FILER_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_FILER_ARRAY_DESCRIPTOR =
|
||||||
"$COMPONENTS_PATH/Filter;"
|
"[$COMPONENTS_PATH/Filter;"
|
||||||
|
|
||||||
|
private lateinit var filterArrayMethod: MutableMethod
|
||||||
|
private var filterCount = 0
|
||||||
|
|
||||||
internal lateinit var addFilter: (String) -> Unit
|
internal lateinit var addFilter: (String) -> Unit
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private lateinit var emptyComponentMethod: MutableMethod
|
|
||||||
|
|
||||||
private lateinit var emptyComponentLabel: String
|
|
||||||
private lateinit var emptyComponentMethodName: String
|
|
||||||
|
|
||||||
private lateinit var pathBuilderMethodCall: String
|
|
||||||
|
|
||||||
private var filterCount = 0
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
|
||||||
SetByteBufferFingerprint.resultOrThrow().let {
|
// region Pass the buffer into Integrations.
|
||||||
it.mutableMethod.apply {
|
|
||||||
val insertIndex = getTargetIndexOrThrow(Opcode.IF_EQZ) + 1
|
|
||||||
|
|
||||||
addInstruction(
|
ByteBufferFingerprint.resultOrThrow().mutableMethod.addInstruction(
|
||||||
insertIndex,
|
0,
|
||||||
"invoke-static { p2 }, $INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V"
|
"invoke-static { p2 }, $INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
var (emptyComponentMethod, emptyComponentLabel) =
|
||||||
EmptyComponentsFingerprint.resultOrThrow().let {
|
EmptyComponentsFingerprint.resultOrThrow().let {
|
||||||
it.mutableMethod.apply {
|
|
||||||
// resolves fingerprint.
|
|
||||||
PathBuilderFingerprint.resolve(context, it.classDef)
|
PathBuilderFingerprint.resolve(context, it.classDef)
|
||||||
emptyComponentMethod = this
|
with(it.mutableMethod) {
|
||||||
emptyComponentMethodName = name
|
|
||||||
|
|
||||||
val emptyComponentMethodIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
val emptyComponentMethodIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||||
val emptyComponentMethodReference =
|
val emptyComponentMethodReference =
|
||||||
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex).reference
|
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex).reference
|
||||||
val emptyComponentFieldReference =
|
val emptyComponentFieldReference =
|
||||||
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex + 2).reference
|
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex + 2).reference
|
||||||
|
|
||||||
emptyComponentLabel = """
|
val label = """
|
||||||
move-object/from16 v0, p1
|
move-object/from16 v0, p1
|
||||||
invoke-static {v0}, $emptyComponentMethodReference
|
invoke-static {v0}, $emptyComponentMethodReference
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
iget-object v0, v0, $emptyComponentFieldReference
|
iget-object v0, v0, $emptyComponentFieldReference
|
||||||
return-object v0
|
return-object v0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
Pair(this, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PathBuilderFingerprint.resultOrThrow().let {
|
fun checkMethodSignatureMatch(pathBuilder: MutableMethod) = emptyComponentMethod.apply {
|
||||||
it.mutableMethod.apply {
|
if (!MethodUtil.methodSignaturesMatch(pathBuilder, this)) {
|
||||||
// If the EmptyComponents Method and the PathBuilder Method are different,
|
implementation!!.instructions
|
||||||
// new inject way is required.
|
.withIndex()
|
||||||
// TODO: Refactor LithoFilter patch when support for YouTube 18.29.38 ~ 19.17.41 and YT Music 6.29.58 ~ 6.51.53 is dropped.
|
.filter { (_, instruction) ->
|
||||||
if (emptyComponentMethodName != name) {
|
val reference = (instruction as? ReferenceInstruction)?.reference
|
||||||
// In this case, the access modifier of the method that handles PathBuilder is 'AccessFlags.PRIVATE or AccessFlags.FINAL.
|
reference is MethodReference &&
|
||||||
// Methods that handle PathBuilder are invoked by methods that handle EmptyComponents.
|
MethodUtil.methodSignaturesMatch(pathBuilder, reference)
|
||||||
// 'pathBuilderMethodCall' is a reference that invokes the PathBuilder Method.
|
|
||||||
pathBuilderMethodCall = "$definingClass->$name("
|
|
||||||
for (i in 0 until parameters.size) {
|
|
||||||
pathBuilderMethodCall += parameterTypes[i]
|
|
||||||
}
|
}
|
||||||
pathBuilderMethodCall += ")$returnType"
|
.map { (index, _) -> index }
|
||||||
|
.reversed()
|
||||||
emptyComponentMethod.apply {
|
.forEach {
|
||||||
// If the return value of the PathBuilder Method is null,
|
|
||||||
// it means that pathBuilder has been filtered by the LithoFilterPatch.
|
|
||||||
// (Refer comments below.)
|
|
||||||
// Returns emptyComponents.
|
|
||||||
for (index in implementation!!.instructions.size - 1 downTo 0) {
|
|
||||||
val instruction = getInstruction(index)
|
|
||||||
if ((instruction as? ReferenceInstruction)?.reference.toString() != pathBuilderMethodCall)
|
|
||||||
continue
|
|
||||||
|
|
||||||
val insertRegister =
|
val insertRegister =
|
||||||
getInstruction<OneRegisterInstruction>(index + 1).registerA
|
getInstruction<OneRegisterInstruction>(it + 1).registerA
|
||||||
val insertIndex = index + 2
|
val insertIndex = it + 2
|
||||||
|
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
insertIndex, """
|
insertIndex, """
|
||||||
@ -124,31 +108,37 @@ object LithoFilterPatch : BytecodePatch(
|
|||||||
ExternalLabel("ignore", getInstruction(insertIndex))
|
ExternalLabel("ignore", getInstruction(insertIndex))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If the EmptyComponents Method and the PathBuilder Method are different,
|
|
||||||
// PathBuilder Method's returnType cannot cast emptyComponents.
|
|
||||||
// So just returns null value.
|
|
||||||
emptyComponentLabel = """
|
emptyComponentLabel = """
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
return-object v0
|
return-object v0
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val stringBuilderIndex =
|
PathBuilderFingerprint.resultOrThrow().let {
|
||||||
getTargetIndexWithFieldReferenceTypeOrThrow("Ljava/lang/StringBuilder;")
|
it.mutableMethod.apply {
|
||||||
|
checkMethodSignatureMatch(this)
|
||||||
|
|
||||||
|
val stringBuilderIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.IPUT_OBJECT &&
|
||||||
|
getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;"
|
||||||
|
}
|
||||||
val stringBuilderRegister =
|
val stringBuilderRegister =
|
||||||
getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
|
getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
|
||||||
|
|
||||||
val emptyStringIndex = getStringInstructionIndex("")
|
val emptyStringIndex = getStringInstructionIndex("")
|
||||||
|
val identifierRegister = getInstruction<TwoRegisterInstruction>(
|
||||||
val identifierIndex =
|
indexOfFirstInstructionReversedOrThrow(emptyStringIndex) {
|
||||||
getTargetIndexReversedOrThrow(emptyStringIndex, Opcode.IPUT_OBJECT)
|
opcode == Opcode.IPUT_OBJECT
|
||||||
val identifierRegister =
|
&& getReference<FieldReference>()?.type == "Ljava/lang/String;"
|
||||||
getInstruction<TwoRegisterInstruction>(identifierIndex).registerA
|
}
|
||||||
|
).registerA
|
||||||
val objectIndex = getTargetIndexOrThrow(emptyStringIndex, Opcode.INVOKE_VIRTUAL)
|
val objectRegister = getInstruction<FiveRegisterInstruction>(
|
||||||
val objectRegister = getInstruction<BuilderInstruction35c>(objectIndex).registerC
|
indexOfFirstInstructionOrThrow(emptyStringIndex) {
|
||||||
|
opcode == Opcode.INVOKE_VIRTUAL
|
||||||
|
}
|
||||||
|
).registerC
|
||||||
|
|
||||||
val insertIndex = stringBuilderIndex + 1
|
val insertIndex = stringBuilderIndex + 1
|
||||||
|
|
||||||
@ -163,30 +153,69 @@ object LithoFilterPatch : BytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LithoFilterPatchConstructorFingerprint.resultOrThrow().let {
|
// Create a new method to get the filter array to avoid register conflicts.
|
||||||
it.mutableMethod.apply {
|
// This fixes an issue with Integrations compiled with Android Gradle Plugin 8.3.0+.
|
||||||
removeInstructions(0, 6)
|
// https://github.com/ReVanced/revanced-patches/issues/2818
|
||||||
|
val lithoFilterMethods = context.findClass(INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR)
|
||||||
|
?.mutableClass
|
||||||
|
?.methods
|
||||||
|
?: throw PatchException("LithoFilterPatch class not found.")
|
||||||
|
|
||||||
|
lithoFilterMethods
|
||||||
|
.first { it.name == "<clinit>" }
|
||||||
|
.apply {
|
||||||
|
val setArrayIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.SPUT_OBJECT &&
|
||||||
|
getReference<FieldReference>()?.type == INTEGRATIONS_FILER_ARRAY_DESCRIPTOR
|
||||||
|
}
|
||||||
|
val setArrayRegister =
|
||||||
|
getInstruction<OneRegisterInstruction>(setArrayIndex).registerA
|
||||||
|
val addedMethodName = "getFilterArray"
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
setArrayIndex, """
|
||||||
|
invoke-static {}, $INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR->$addedMethodName()$INTEGRATIONS_FILER_ARRAY_DESCRIPTOR
|
||||||
|
move-result-object v$setArrayRegister
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
filterArrayMethod = ImmutableMethod(
|
||||||
|
definingClass,
|
||||||
|
addedMethodName,
|
||||||
|
emptyList(),
|
||||||
|
INTEGRATIONS_FILER_ARRAY_DESCRIPTOR,
|
||||||
|
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(3),
|
||||||
|
).toMutable().apply {
|
||||||
|
addInstruction(
|
||||||
|
0,
|
||||||
|
"return-object v2"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lithoFilterMethods.add(filterArrayMethod)
|
||||||
|
}
|
||||||
|
|
||||||
addFilter = { classDescriptor ->
|
addFilter = { classDescriptor ->
|
||||||
addInstructions(
|
filterArrayMethod.addInstructions(
|
||||||
0, """
|
0,
|
||||||
|
"""
|
||||||
new-instance v0, $classDescriptor
|
new-instance v0, $classDescriptor
|
||||||
invoke-direct {v0}, $classDescriptor-><init>()V
|
invoke-direct {v0}, $classDescriptor-><init>()V
|
||||||
const/16 v3, ${filterCount++}
|
const/16 v1, ${filterCount++}
|
||||||
aput-object v0, v2, v3
|
aput-object v0, v2, v1
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() = LithoFilterPatchConstructorFingerprint.result!!
|
override fun close() = filterArrayMethod.addInstructions(
|
||||||
.mutableMethod.addInstructions(
|
0,
|
||||||
0, """
|
"""
|
||||||
const/16 v1, $filterCount
|
const/16 v0, $filterCount
|
||||||
new-array v2, v1, [$INTEGRATIONS_FILER_CLASS_DESCRIPTOR
|
new-array v2, v0, $INTEGRATIONS_FILER_ARRAY_DESCRIPTOR
|
||||||
const/4 v1, 0x1
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
|||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object SetByteBufferFingerprint : MethodFingerprint(
|
internal object ByteBufferFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf("I", "Ljava/nio/ByteBuffer;"),
|
parameters = listOf("I", "Ljava/nio/ByteBuffer;"),
|
@ -1,14 +0,0 @@
|
|||||||
package app.revanced.patches.shared.litho.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
|
|
||||||
internal object LithoFilterPatchConstructorFingerprint : MethodFingerprint(
|
|
||||||
returnType = "V",
|
|
||||||
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
customFingerprint = { methodDef, _ ->
|
|
||||||
methodDef.definingClass == "$COMPONENTS_PATH/LithoFilterPatch;"
|
|
||||||
}
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user