mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 01:51:27 +01:00
feat(TikTok): Add Sanitize sharing links patch (#6176)
This commit is contained in:
@@ -17,9 +17,6 @@ public class LinkSanitizer {
|
||||
|
||||
public LinkSanitizer(String ... parametersToRemove) {
|
||||
final int parameterCount = parametersToRemove.length;
|
||||
if (parameterCount == 0) {
|
||||
throw new IllegalArgumentException("No parameters specified");
|
||||
}
|
||||
|
||||
// List is faster if only checking a few parameters.
|
||||
this.parametersToRemove = parameterCount > 4
|
||||
@@ -40,6 +37,7 @@ public class LinkSanitizer {
|
||||
try {
|
||||
Uri.Builder builder = uri.buildUpon().clearQuery();
|
||||
|
||||
if (!parametersToRemove.isEmpty()) {
|
||||
for (String paramName : uri.getQueryParameterNames()) {
|
||||
if (!parametersToRemove.contains(paramName)) {
|
||||
for (String value : uri.getQueryParameters(paramName)) {
|
||||
@@ -47,6 +45,7 @@ public class LinkSanitizer {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uri sanitizedUrl = builder.build();
|
||||
Logger.printInfo(() -> "Sanitized url: " + uri + " to: " + sanitizedUrl);
|
||||
|
||||
@@ -23,6 +23,12 @@ public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory {
|
||||
public void addPreferences(Context context) {
|
||||
addPreference(new ReVancedTikTokAboutPreference(context));
|
||||
|
||||
addPreference(new TogglePreference(context,
|
||||
"Sanitize sharing links",
|
||||
"Remove tracking parameters from shared links.",
|
||||
BaseSettings.SANITIZE_SHARED_LINKS
|
||||
));
|
||||
|
||||
addPreference(new TogglePreference(context,
|
||||
"Enable debug log",
|
||||
"Show extension debug log.",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package app.revanced.extension.tiktok.share;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.privacy.LinkSanitizer;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ShareUrlSanitizer {
|
||||
|
||||
private static final LinkSanitizer sanitizer = new LinkSanitizer();
|
||||
|
||||
/**
|
||||
* Injection point for setting check.
|
||||
*/
|
||||
public static boolean shouldSanitize() {
|
||||
return BaseSettings.SANITIZE_SHARED_LINKS.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point for URL sanitization.
|
||||
*/
|
||||
public static String sanitizeShareUrl(final String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return sanitizer.sanitizeUrlString(url);
|
||||
}
|
||||
}
|
||||
@@ -1188,6 +1188,10 @@ public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt {
|
||||
public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatchKt {
|
||||
public static final fun getSanitizeShareUrlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatchKt {
|
||||
public static final fun getSpoofSimPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.tiktok.misc.share
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val urlShorteningFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)
|
||||
returns("LX/")
|
||||
parameters(
|
||||
"I",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
opcodes(Opcode.RETURN_OBJECT)
|
||||
|
||||
// Same Kotlin intrinsics literal on both variants.
|
||||
strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)")
|
||||
|
||||
custom { method, _ ->
|
||||
// LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically.
|
||||
method.name == "LIZLLL"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package app.revanced.patches.tiktok.misc.share
|
||||
|
||||
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.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS
|
||||
import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS
|
||||
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
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.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/tiktok/share/ShareUrlSanitizer;"
|
||||
|
||||
@Suppress("unused")
|
||||
val sanitizeShareUrlsPatch = bytecodePatch(
|
||||
name = PATCH_NAME_SANITIZE_SHARING_LINKS,
|
||||
description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS,
|
||||
) {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
compatibleWith(
|
||||
"com.ss.android.ugc.trill"("36.5.4"),
|
||||
"com.zhiliaoapp.musically"("36.5.4"),
|
||||
)
|
||||
|
||||
execute {
|
||||
urlShorteningFingerprint.method.apply {
|
||||
val invokeIndex = indexOfFirstInstructionOrThrow {
|
||||
val ref = getReference<MethodReference>()
|
||||
ref?.name == "LIZ" && ref.definingClass.startsWith("LX/")
|
||||
}
|
||||
|
||||
val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT)
|
||||
val urlRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
|
||||
// Resolve Observable wrapper classes at runtime
|
||||
val observableWrapperIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_INSTANCE)
|
||||
val observableWrapperClass = getInstruction<ReferenceInstruction>(observableWrapperIndex)
|
||||
.reference.toString()
|
||||
|
||||
val observableFactoryIndex = indexOfFirstInstructionOrThrow {
|
||||
val ref = getReference<MethodReference>()
|
||||
ref?.name == "LJ" && ref.definingClass.startsWith("LX/")
|
||||
}
|
||||
val observableFactoryRef = getInstruction<ReferenceInstruction>(observableFactoryIndex)
|
||||
.reference as MethodReference
|
||||
|
||||
val observableFactoryClass = observableFactoryRef.definingClass
|
||||
val observableInterfaceType = observableFactoryRef.parameterTypes.first()
|
||||
val observableReturnType = observableFactoryRef.returnType
|
||||
|
||||
val wrapperRegister = findFreeRegister(moveResultIndex + 1, urlRegister)
|
||||
|
||||
// Check setting and conditionally sanitize share URL.
|
||||
addInstructionsWithLabels(
|
||||
moveResultIndex + 1,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldSanitize()Z
|
||||
move-result v$wrapperRegister
|
||||
if-eqz v$wrapperRegister, :skip_sanitization
|
||||
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$urlRegister
|
||||
|
||||
# Wrap sanitized URL and return early to bypass ShareExtService
|
||||
new-instance v$wrapperRegister, $observableWrapperClass
|
||||
invoke-direct { v$wrapperRegister, v$urlRegister }, $observableWrapperClass-><init>(Ljava/lang/String;)V
|
||||
invoke-static { v$wrapperRegister }, $observableFactoryClass->LJ($observableInterfaceType)$observableReturnType
|
||||
move-result-object v$urlRegister
|
||||
return-object v$urlRegister
|
||||
|
||||
:skip_sanitization
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user