feat: add capability to filter from protobuf buffer

This commit is contained in:
oSumAtrIX 2023-05-09 08:17:05 +02:00
parent 67d237c23f
commit b738b6bf35
2 changed files with 79 additions and 19 deletions

View file

@ -0,0 +1,25 @@
package app.revanced.patches.youtube.misc.litho.filter.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object ProtobufBufferFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.IGET_OBJECT, // References the field required below.
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
Opcode.IF_NEZ,
Opcode.CONST_4,
Opcode.GOTO,
Opcode.CHECK_CAST, // Casts the referenced field to a specific type that stores the protobuf buffer.
Opcode.INVOKE_VIRTUAL
)
)

View file

@ -17,6 +17,7 @@ import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ComponentContextParserFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.EmptyComponentBuilderFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ProtobufBufferFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ReadComponentIdentifierFingerprint
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@ -32,7 +33,9 @@ class LithoFilterPatch : BytecodePatch(
) {
override fun execute(context: BytecodeContext): PatchResult {
ComponentContextParserFingerprint.result?.also {
arrayOf(EmptyComponentBuilderFingerprint, ReadComponentIdentifierFingerprint).forEach { fingerprint ->
arrayOf(
EmptyComponentBuilderFingerprint, ReadComponentIdentifierFingerprint, ProtobufBufferFingerprint
).forEach { fingerprint ->
if (!fingerprint.resolve(context, it.mutableMethod, it.mutableClass))
return fingerprint.toErrorResult()
}
@ -45,26 +48,54 @@ class LithoFilterPatch : BytecodePatch(
val builderMethodDescriptor = instruction(builderMethodIndex).descriptor
val emptyComponentFieldDescriptor = instruction(emptyComponentFieldIndex).descriptor
// Register is overwritten right after it is used in this patch, therefore free to clobber.
val clobberedRegister = instruction(insertHookIndex - 1).twoRegisterA
val free = instruction<TwoRegisterInstruction>(insertHookIndex - 1).registerA
val free2 = instruction<OneRegisterInstruction>(insertHookIndex).registerA
@Suppress("UnnecessaryVariable")
// The register, this patch clobbers, is previously used for the StringBuilder,
// later on a new StringBuilder is instantiated on it.
val stringBuilderRegister = clobberedRegister
val stringBuilderRegister = free
val identifierRegister = instruction(ReadComponentIdentifierFingerprint.patternScanEndIndex).oneRegisterA
val identifierRegister =
instruction<OneRegisterInstruction>(ReadComponentIdentifierFingerprint.patternScanEndIndex).registerA
// Parameter that holds a ref to a type with a field that ref the protobuf buffer object.
val protobufParameterNumber = 3
// Get the field that stores an protobuf buffer required below.
val protobufBufferRefTypeRefFieldDescriptor =
instruction(ProtobufBufferFingerprint.patternScanStartIndex).descriptor
val protobufBufferRefTypeDescriptor =
instruction(ProtobufBufferFingerprint.patternScanEndIndex - 1).descriptor
val protobufBufferFieldDescriptor = "$protobufBufferRefTypeDescriptor->b:Ljava/nio/ByteBuffer;"
addInstructions(
insertHookIndex, // right after setting the component.pathBuilder field,
insertHookIndex, // right after setting the component.pathBuilder field.
"""
invoke-static {v$stringBuilderRegister, v$identifierRegister}, Lapp/revanced/integrations/patches/LithoFilterPatch;->filter(Ljava/lang/StringBuilder;Ljava/lang/String;)Z
move-result v$clobberedRegister
if-eqz v$clobberedRegister, :not_an_ad
move-object/from16 v$clobberedRegister, p1
invoke-static {v$clobberedRegister}, $builderMethodDescriptor
move-result-object v$clobberedRegister
iget-object v$clobberedRegister, v$clobberedRegister, $emptyComponentFieldDescriptor
return-object v$clobberedRegister
# Get the protobuf buffer object.
move-object/from16 v$free2, p$protobufParameterNumber
iget-object v$free2, v$free2, $protobufBufferRefTypeRefFieldDescriptor
check-cast v$free2, $protobufBufferRefTypeDescriptor
# Register "free" now holds the protobuf buffer object
iget-object v$free2, v$free2, $protobufBufferFieldDescriptor
# Invoke the filter method.
invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free2 }, $FILTER_METHOD_DESCRIPTOR
move-result v$free
if-eqz v$free, :not_an_ad
# If the filter method returned true, then return a replacement empty component.
move-object/from16 v$free, p1
invoke-static {v$free}, $builderMethodDescriptor
move-result-object v$free
iget-object v$free, v$free, $emptyComponentFieldDescriptor
return-object v$free
""",
listOf(ExternalLabel("not_an_ad", instruction(insertHookIndex)))
)
@ -75,16 +106,20 @@ class LithoFilterPatch : BytecodePatch(
}
private companion object {
private val MethodFingerprint.patternScanResult
get() = result!!.scanResult.patternScanResult!!
val MethodFingerprint.patternScanEndIndex
get() = result!!.scanResult.patternScanResult!!.endIndex
get() = patternScanResult.endIndex
val MethodFingerprint.patternScanStartIndex
get() = patternScanResult.startIndex
val Instruction.descriptor
get() = (this as ReferenceInstruction).reference.toString()
val Instruction.oneRegisterA
get() = (this as OneRegisterInstruction).registerA
val Instruction.twoRegisterA
get() = (this as TwoRegisterInstruction).registerA
const val FILTER_METHOD_DESCRIPTOR =
"Lapp/revanced/integrations/patches/litho/LithoFilterPatch;" +
"->filter(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/nio/ByteBuffer;)Z"
}
}