mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-29 13:20:19 +02:00
feat(Spoof streaming data): Disable PoToken service when is turned on
- Any app clients that use do not need the PoToken generated by DroidGuard - is fetched even when is turned on - DroidGuard VM tries to solve , but some functions are not fully implemented in DroidGuard, resulting in spam logs: https://github.com/microg/GmsCore/issues/2584
This commit is contained in:
parent
bda2ef23cc
commit
ed6acc6197
@ -4,7 +4,6 @@ import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingDa
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Base64;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -35,9 +34,6 @@ public class SpoofStreamingDataPatch {
|
|||||||
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
|
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
|
||||||
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
|
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static volatile String droidGuardPoToken = "";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key: video id
|
* Key: video id
|
||||||
* Value: original video length [streamingData.formats.approxDurationMs]
|
* Value: original video length [streamingData.formats.approxDurationMs]
|
||||||
@ -111,6 +107,16 @@ public class SpoofStreamingDataPatch {
|
|||||||
return SPOOF_STREAMING_DATA;
|
return SPOOF_STREAMING_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static Object isSpoofingEnabled(Object original) {
|
||||||
|
if (!SPOOF_STREAMING_DATA) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
* This method is only invoked when playing a livestream on an iOS client.
|
* This method is only invoked when playing a livestream on an iOS client.
|
||||||
@ -173,7 +179,7 @@ public class SpoofStreamingDataPatch {
|
|||||||
if (SPOOF_STREAMING_DATA_MUSIC) {
|
if (SPOOF_STREAMING_DATA_MUSIC) {
|
||||||
try {
|
try {
|
||||||
if (requestHeader != null) {
|
if (requestHeader != null) {
|
||||||
StreamingDataRequest.fetchRequest(videoId, requestHeader, VISITOR_DATA, PO_TOKEN, droidGuardPoToken);
|
StreamingDataRequest.fetchRequest(videoId, requestHeader, VISITOR_DATA, PO_TOKEN);
|
||||||
} else {
|
} else {
|
||||||
Logger.printDebug(() -> "Ignoring request with no header.");
|
Logger.printDebug(() -> "Ignoring request with no header.");
|
||||||
}
|
}
|
||||||
@ -212,7 +218,7 @@ public class SpoofStreamingDataPatch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamingDataRequest.fetchRequest(id, requestHeaders, VISITOR_DATA, PO_TOKEN, droidGuardPoToken);
|
StreamingDataRequest.fetchRequest(id, requestHeaders, VISITOR_DATA, PO_TOKEN);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "fetchStreams failure", ex);
|
Logger.printException(() -> "fetchStreams failure", ex);
|
||||||
}
|
}
|
||||||
@ -335,17 +341,4 @@ public class SpoofStreamingDataPatch {
|
|||||||
|
|
||||||
return videoFormat;
|
return videoFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
public static void setDroidGuardPoToken(byte[] bytes) {
|
|
||||||
if (SPOOF_STREAMING_DATA && bytes.length > 20) {
|
|
||||||
final String poToken = Base64.encodeToString(bytes, Base64.URL_SAFE);
|
|
||||||
if (!droidGuardPoToken.equals(poToken)) {
|
|
||||||
Logger.printDebug(() -> "New droidGuardPoToken loaded:\n" + poToken);
|
|
||||||
droidGuardPoToken = poToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ import java.util.concurrent.TimeoutException
|
|||||||
* did use its own client streams.
|
* did use its own client streams.
|
||||||
*/
|
*/
|
||||||
class StreamingDataRequest private constructor(
|
class StreamingDataRequest private constructor(
|
||||||
videoId: String, playerHeaders: Map<String, String>, visitorId: String,
|
videoId: String, playerHeaders: Map<String, String>,
|
||||||
botGuardPoToken: String, droidGuardPoToken: String
|
visitorId: String, botGuardPoToken: String
|
||||||
) {
|
) {
|
||||||
private val videoId: String
|
private val videoId: String
|
||||||
private val future: Future<ByteBuffer?>
|
private val future: Future<ByteBuffer?>
|
||||||
@ -47,8 +47,7 @@ class StreamingDataRequest private constructor(
|
|||||||
videoId,
|
videoId,
|
||||||
playerHeaders,
|
playerHeaders,
|
||||||
visitorId,
|
visitorId,
|
||||||
botGuardPoToken,
|
botGuardPoToken
|
||||||
droidGuardPoToken
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,8 +135,8 @@ class StreamingDataRequest private constructor(
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun fetchRequest(
|
fun fetchRequest(
|
||||||
videoId: String, fetchHeaders: Map<String, String>, visitorId: String,
|
videoId: String, fetchHeaders: Map<String, String>,
|
||||||
botGuardPoToken: String, droidGuardPoToken: String
|
visitorId: String, botGuardPoToken: String
|
||||||
) {
|
) {
|
||||||
// Always fetch, even if there is an existing request for the same video.
|
// Always fetch, even if there is an existing request for the same video.
|
||||||
cache[videoId] =
|
cache[videoId] =
|
||||||
@ -145,8 +144,7 @@ class StreamingDataRequest private constructor(
|
|||||||
videoId,
|
videoId,
|
||||||
fetchHeaders,
|
fetchHeaders,
|
||||||
visitorId,
|
visitorId,
|
||||||
botGuardPoToken,
|
botGuardPoToken
|
||||||
droidGuardPoToken
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +158,11 @@ class StreamingDataRequest private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun send(
|
private fun send(
|
||||||
clientType: AppClient.ClientType, videoId: String, playerHeaders: Map<String, String>,
|
clientType: AppClient.ClientType,
|
||||||
visitorId: String, botGuardPoToken: String, droidGuardPoToken: String
|
videoId: String,
|
||||||
|
playerHeaders: Map<String, String>,
|
||||||
|
visitorId: String,
|
||||||
|
botGuardPoToken: String
|
||||||
): HttpURLConnection? {
|
): HttpURLConnection? {
|
||||||
Objects.requireNonNull(clientType)
|
Objects.requireNonNull(clientType)
|
||||||
Objects.requireNonNull(videoId)
|
Objects.requireNonNull(videoId)
|
||||||
@ -210,10 +211,7 @@ class StreamingDataRequest private constructor(
|
|||||||
visitorId = visitorId,
|
visitorId = visitorId,
|
||||||
setLocale = setLocale
|
setLocale = setLocale
|
||||||
)
|
)
|
||||||
if (droidGuardPoToken.isNotEmpty()) {
|
Logger.printDebug { "Set poToken (botGuardPoToken):\n$botGuardPoToken" }
|
||||||
Logger.printDebug { "Original poToken (droidGuardPoToken):\n$droidGuardPoToken" }
|
|
||||||
}
|
|
||||||
Logger.printDebug { "Replaced poToken (botGuardPoToken):\n$botGuardPoToken" }
|
|
||||||
} else {
|
} else {
|
||||||
requestBody =
|
requestBody =
|
||||||
createApplicationRequestBody(
|
createApplicationRequestBody(
|
||||||
@ -249,8 +247,8 @@ class StreamingDataRequest private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fetch(
|
private fun fetch(
|
||||||
videoId: String, playerHeaders: Map<String, String>, visitorId: String,
|
videoId: String, playerHeaders: Map<String, String>,
|
||||||
botGuardPoToken: String, droidGuardPoToken: String
|
visitorId: String, botGuardPoToken: String
|
||||||
): ByteBuffer? {
|
): ByteBuffer? {
|
||||||
lastSpoofedClientType = null
|
lastSpoofedClientType = null
|
||||||
|
|
||||||
@ -267,8 +265,7 @@ class StreamingDataRequest private constructor(
|
|||||||
videoId,
|
videoId,
|
||||||
playerHeaders,
|
playerHeaders,
|
||||||
visitorId,
|
visitorId,
|
||||||
botGuardPoToken,
|
botGuardPoToken
|
||||||
droidGuardPoToken
|
|
||||||
)?.let { connection ->
|
)?.let { connection ->
|
||||||
try {
|
try {
|
||||||
// gzip encoding doesn't response with content length (-1),
|
// gzip encoding doesn't response with content length (-1),
|
||||||
|
@ -21,6 +21,7 @@ import app.revanced.util.fingerprint.matchOrThrow
|
|||||||
import app.revanced.util.fingerprint.methodOrThrow
|
import app.revanced.util.fingerprint.methodOrThrow
|
||||||
import app.revanced.util.fingerprint.mutableClassOrThrow
|
import app.revanced.util.fingerprint.mutableClassOrThrow
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.getWalkerMethod
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
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
|
||||||
@ -103,9 +104,9 @@ fun baseSpoofStreamingDataPatch(
|
|||||||
"fetchStreams(Ljava/lang/String;Ljava/util/Map;)V"
|
"fetchStreams(Ljava/lang/String;Ljava/util/Map;)V"
|
||||||
|
|
||||||
if (entrySetIndex < 0) smaliInstructions = """
|
if (entrySetIndex < 0) smaliInstructions = """
|
||||||
move-object/from16 v$mapRegister, p1
|
move-object/from16 v$mapRegister, p1
|
||||||
|
|
||||||
""" + smaliInstructions
|
""" + smaliInstructions
|
||||||
|
|
||||||
// Copy request headers for streaming data fetch.
|
// Copy request headers for streaming data fetch.
|
||||||
addInstructions(newRequestBuilderIndex + 2, smaliInstructions)
|
addInstructions(newRequestBuilderIndex + 2, smaliInstructions)
|
||||||
@ -366,25 +367,38 @@ fun baseSpoofStreamingDataPatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Set DroidGuard poToken.
|
|
||||||
|
|
||||||
poTokenToStringFingerprint.mutableClassOrThrow().let {
|
|
||||||
val poTokenClass = it.fields.find { field ->
|
|
||||||
field.accessFlags == AccessFlags.PRIVATE.value && field.type.startsWith("L")
|
|
||||||
}!!.type
|
|
||||||
|
|
||||||
findMethodOrThrow(poTokenClass) {
|
|
||||||
name == "<init>" &&
|
|
||||||
parameters == listOf("[B")
|
|
||||||
}.addInstruction(
|
|
||||||
1,
|
|
||||||
"invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->setDroidGuardPoToken([B)V"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
executeBlock()
|
executeBlock()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize {
|
||||||
|
gmsServiceBrokerFingerprint.methodOrThrow()
|
||||||
|
.addInstructionsWithLabels(
|
||||||
|
0, """
|
||||||
|
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :ignore
|
||||||
|
return-void
|
||||||
|
:ignore
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
gmsServiceBrokerExceptionFingerprint.matchOrThrow().let {
|
||||||
|
val walkerIndex = it.patternMatch!!.startIndex
|
||||||
|
val walkerMethod = it.getWalkerMethod(walkerIndex)
|
||||||
|
|
||||||
|
walkerMethod.apply {
|
||||||
|
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.CHECK_CAST)
|
||||||
|
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
insertIndex + 1, """
|
||||||
|
invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
move-result-object v$insertRegister
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,21 +198,20 @@ internal val hlsCurrentTimeFingerprint = legacyFingerprint(
|
|||||||
literals = listOf(HLS_CURRENT_TIME_FEATURE_FLAG),
|
literals = listOf(HLS_CURRENT_TIME_FEATURE_FLAG),
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val poTokenToStringFingerprint = legacyFingerprint(
|
internal val gmsServiceBrokerFingerprint = legacyFingerprint(
|
||||||
name = "poTokenToStringFingerprint",
|
name = "gmsServiceBrokerFingerprint",
|
||||||
returnType = "Ljava/lang/String;",
|
returnType = "V",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
strings = listOf("mServiceBroker is null, client disconnected")
|
||||||
parameters = emptyList(),
|
|
||||||
strings = listOf("UTF-8"),
|
|
||||||
customFingerprint = { method, classDef ->
|
|
||||||
method.name == "toString" &&
|
|
||||||
classDef.fields.find { it.type == "[B" } != null &&
|
|
||||||
// In YouTube, this field's type is 'Lcom/google/android/gms/potokens/PoToken;'.
|
|
||||||
// In YouTube Music, this class name is obfuscated.
|
|
||||||
classDef.fields.find {
|
|
||||||
it.accessFlags == AccessFlags.PRIVATE.value && it.type.startsWith(
|
|
||||||
"L"
|
|
||||||
)
|
|
||||||
} != null
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val gmsServiceBrokerExceptionFingerprint = legacyFingerprint(
|
||||||
|
name = "gmsServiceBrokerExceptionFingerprint",
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("Ljava/lang/Exception;"),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
),
|
||||||
|
strings = listOf("Exception must not be null")
|
||||||
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user