-
-
Notifications
You must be signed in to change notification settings - Fork 443
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package app.revanced.patches.youtube.misc.litho.filter.fingerprints | ||
|
||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint | ||
|
||
object LithoFilterFingerprint : MethodFingerprint( | ||
customFingerprint = custom@{ method, classDef -> | ||
if (method.name != "<clinit>") return@custom false | ||
|
||
classDef.type.endsWith("LithoFilterPatch;") | ||
} | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,11 @@ import app.revanced.extensions.toErrorResult | |
import app.revanced.patcher.annotation.Description | ||
import app.revanced.patcher.annotation.Version | ||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions | ||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction | ||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint | ||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve | ||
import app.revanced.patcher.patch.BytecodePatch | ||
|
@@ -15,46 +18,58 @@ import app.revanced.patcher.patch.annotations.DependsOn | |
import app.revanced.patcher.util.smali.ExternalLabel | ||
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 app.revanced.patches.youtube.misc.litho.filter.fingerprints.* | ||
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction | ||
import org.jf.dexlib2.iface.instruction.Instruction | ||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction | ||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction | ||
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction | ||
import java.io.Closeable | ||
|
||
@DependsOn([IntegrationsPatch::class]) | ||
@Description("Hooks the method which parses the bytes into a ComponentContext to filter components.") | ||
@LithoFilterCompatibility | ||
@Version("0.0.1") | ||
class LithoFilterPatch : BytecodePatch( | ||
listOf(ComponentContextParserFingerprint) | ||
) { | ||
listOf(ComponentContextParserFingerprint, LithoFilterFingerprint) | ||
), Closeable { | ||
override fun execute(context: BytecodeContext): PatchResult { | ||
ComponentContextParserFingerprint.result?.also { | ||
arrayOf( | ||
EmptyComponentBuilderFingerprint, ReadComponentIdentifierFingerprint, ProtobufBufferFingerprint | ||
EmptyComponentBuilderFingerprint, | ||
ReadComponentIdentifierFingerprint, | ||
ProtobufBufferFingerprint | ||
).forEach { fingerprint -> | ||
if (!fingerprint.resolve(context, it.mutableMethod, it.mutableClass)) | ||
return fingerprint.toErrorResult() | ||
if (fingerprint.resolve(context, it.mutableMethod, it.mutableClass)) return@forEach | ||
return fingerprint.toErrorResult() | ||
} | ||
}?.let { result -> | ||
val builderMethodIndex = EmptyComponentBuilderFingerprint.patternScanEndIndex | ||
val emptyComponentFieldIndex = builderMethodIndex + 2 | ||
|
||
result.mutableMethod.apply { | ||
val insertHookIndex = result.scanResult.patternScanResult!!.endIndex | ||
val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor | ||
val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor | ||
// Register is overwritten right after it is used in this patch, therefore free to clobber. | ||
val free = getInstruction<TwoRegisterInstruction>(insertHookIndex - 1).registerA | ||
val free2 = getInstruction<OneRegisterInstruction>(insertHookIndex).registerA | ||
|
||
// region Get free registers that this patch uses | ||
// Registers are overwritten right after they are used in this patch, therefore free to clobber. | ||
|
||
val freeRegistersInstruction = getInstruction<FiveRegisterInstruction>(insertHookIndex - 2) | ||
|
||
// Later used to store the protobuf buffer object. | ||
val free1 = getInstruction<OneRegisterInstruction>(insertHookIndex).registerA | ||
// Later used to store the identifier of the component. | ||
// This register currently holds a reference to the StringBuilder object | ||
// that is required before clobbering. | ||
val free2 = freeRegistersInstruction.registerC | ||
|
||
@Suppress("UnnecessaryVariable") | ||
// The register, this patch clobbers, is previously used for the StringBuilder, | ||
// later on a new StringBuilder is instantiated on it. | ||
val stringBuilderRegister = free | ||
val stringBuilderRegister = free2 | ||
|
||
// endregion | ||
|
||
// region Get references that this patch needs | ||
|
||
val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor | ||
val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor | ||
|
||
val identifierRegister = | ||
getInstruction<OneRegisterInstruction>(ReadComponentIdentifierFingerprint.patternScanEndIndex).registerA | ||
|
@@ -69,57 +84,87 @@ class LithoFilterPatch : BytecodePatch( | |
getInstruction(ProtobufBufferFingerprint.patternScanEndIndex - 1).descriptor | ||
val protobufBufferFieldDescriptor = "$protobufBufferRefTypeDescriptor->b:Ljava/nio/ByteBuffer;" | ||
|
||
// endregion | ||
|
||
// region Patch the method | ||
|
||
// Insert the instructions that are responsible | ||
// to return an EmptyComponent instead of the original component if the filter method returns false. | ||
addInstructionsWithLabels( | ||
insertHookIndex, // right after setting the component.pathBuilder field. | ||
insertHookIndex, | ||
""" | ||
# Get the protobuf buffer object. | ||
move-object/from16 v$free2, p$protobufParameterNumber | ||
iget-object v$free2, v$free2, $protobufBufferRefTypeRefFieldDescriptor | ||
check-cast v$free2, $protobufBufferRefTypeDescriptor | ||
move-object/from16 v$free1, p$protobufParameterNumber | ||
iget-object v$free1, v$free1, $protobufBufferRefTypeRefFieldDescriptor | ||
check-cast v$free1, $protobufBufferRefTypeDescriptor | ||
# Register "free" now holds the protobuf buffer object | ||
iget-object v$free2, v$free2, $protobufBufferFieldDescriptor | ||
iget-object v$free1, v$free1, $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 | ||
invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free1 }, $FILTER_METHOD_DESCRIPTOR | ||
move-result v$free1 | ||
# 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 | ||
if-eqz v$free1, :unfiltered | ||
move-object/from16 v$free2, p1 | ||
invoke-static {v$free2}, $builderMethodDescriptor | ||
move-result-object v$free2 | ||
iget-object v$free2, v$free2, $emptyComponentFieldDescriptor | ||
return-object v$free2 | ||
""", | ||
ExternalLabel("not_an_ad", getInstruction(insertHookIndex)) | ||
// Used to jump over the instruction which block the component from being created. | ||
ExternalLabel("unfiltered", getInstruction(insertHookIndex)) | ||
) | ||
// endregion | ||
} | ||
} ?: return ComponentContextParserFingerprint.toErrorResult() | ||
|
||
LithoFilterFingerprint.result?.mutableMethod?.apply { | ||
removeInstructions(2, 4) // Remove dummy filter. | ||
|
||
addFilter = { classDescriptor -> | ||
addInstructions( | ||
2, | ||
""" | ||
new-instance v1, $classDescriptor | ||
invoke-direct {v1}, $classDescriptor-><init>()V | ||
const/4 v2, ${filterCount++} | ||
aput-object v1, v0, v2 | ||
""" | ||
) | ||
} | ||
} ?: return LithoFilterFingerprint.toErrorResult() | ||
|
||
return PatchResultSuccess() | ||
} | ||
|
||
private companion object { | ||
override fun close() = LithoFilterFingerprint.result!! | ||
.mutableMethod.replaceInstruction(0, "const/4 v0, $filterCount") | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
oSumAtrIX
Author
Member
|
||
|
||
companion object { | ||
private val MethodFingerprint.patternScanResult | ||
get() = result!!.scanResult.patternScanResult!! | ||
|
||
val MethodFingerprint.patternScanEndIndex | ||
private val MethodFingerprint.patternScanEndIndex | ||
get() = patternScanResult.endIndex | ||
|
||
val MethodFingerprint.patternScanStartIndex | ||
private val MethodFingerprint.patternScanStartIndex | ||
get() = patternScanResult.startIndex | ||
|
||
val Instruction.descriptor | ||
private val Instruction.descriptor | ||
get() = (this as ReferenceInstruction).reference.toString() | ||
|
||
const val FILTER_METHOD_DESCRIPTOR = | ||
private const val FILTER_METHOD_DESCRIPTOR = | ||
"Lapp/revanced/integrations/patches/components/LithoFilterPatch;" + | ||
"->filter(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/nio/ByteBuffer;)Z" | ||
|
||
internal lateinit var addFilter: (String) -> Unit | ||
private set | ||
|
||
private var filterCount = 0 | ||
} | ||
} |
2 comments
on commit fc69491
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comments
filter is missing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johnconner122 Thanks, please open an issue or PR
If the number of filters exceeds 8, an exception is raised
Because
const/4 v0, 0x8
is an invalid valueThis may not be an issue right now, but I think it would be better to use
const/16 v0, $filterCount
instead ofconst/4 v0, $filterCount