mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-29 13:41:03 +00:00
fix: Fix accidental changes
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
package app.revanced.patches.all.misc.spoof
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
|
import app.revanced.patcher.patch.stringOption
|
||||||
|
import app.revanced.util.getNode
|
||||||
|
import com.android.apksig.ApkVerifier
|
||||||
|
import com.android.apksig.apk.ApkFormatException
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.InvalidPathException
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.cert.CertificateException
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val enableRomSignatureSpoofing = resourcePatch(
|
||||||
|
name = "Enable ROM signature spoofing",
|
||||||
|
description = "Spoofs the signature via the manifest meta-data \"fake-signature\". " +
|
||||||
|
"This patch only works with ROMs that support signature spoofing.",
|
||||||
|
use = false,
|
||||||
|
) {
|
||||||
|
val signatureOrPath by stringOption(
|
||||||
|
key = "signatureOrApkFilePath",
|
||||||
|
title = "Signature or APK file path",
|
||||||
|
validator = validator@{ signature ->
|
||||||
|
signature ?: return@validator false
|
||||||
|
|
||||||
|
parseSignature(signature) != null
|
||||||
|
},
|
||||||
|
description = "The hex-encoded signature or path to an APK file with the desired signature.",
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
execute {
|
||||||
|
document("AndroidManifest.xml").use { document ->
|
||||||
|
val permission = document.createElement("uses-permission").apply {
|
||||||
|
setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE")
|
||||||
|
}
|
||||||
|
val manifest = document.getNode("manifest").appendChild(permission)
|
||||||
|
|
||||||
|
|
||||||
|
val fakeSignatureMetadata = document.createElement("meta-data").apply {
|
||||||
|
setAttribute("android:name", "fake-signature")
|
||||||
|
setAttribute("android:value", parseSignature(signatureOrPath!!))
|
||||||
|
}
|
||||||
|
document.getNode("application").appendChild(fakeSignatureMetadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseSignature(optionValue: String): String? {
|
||||||
|
// Parse as a hex-encoded signature.
|
||||||
|
try {
|
||||||
|
// TODO: Replace with signature.hexToByteArray when stable in kotlin
|
||||||
|
val signatureBytes = HexFormat.of().parseHex(optionValue)
|
||||||
|
CertificateFactory.getInstance("X.509").generateCertificate(signatureBytes.inputStream())
|
||||||
|
|
||||||
|
return optionValue
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
} catch (_: CertificateException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as a path to an APK file.
|
||||||
|
try {
|
||||||
|
val apkFile = File(optionValue)
|
||||||
|
if (!apkFile.isFile) return null
|
||||||
|
|
||||||
|
val result = ApkVerifier.Builder(apkFile).build().verify()
|
||||||
|
|
||||||
|
val hexFormat = HexFormat.of()
|
||||||
|
|
||||||
|
val signature = (if (result.isVerifiedUsingV3Scheme) {
|
||||||
|
result.v3SchemeSigners[0].certificate
|
||||||
|
} else if (result.isVerifiedUsingV2Scheme) {
|
||||||
|
result.v2SchemeSigners[0].certificate
|
||||||
|
} else if (result.isVerifiedUsingV1Scheme) {
|
||||||
|
result.v1SchemeSigners[0].certificate
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}).encoded
|
||||||
|
|
||||||
|
return hexFormat.formatHex(signature)
|
||||||
|
} catch (_: IOException) {
|
||||||
|
} catch (_: InvalidPathException) {
|
||||||
|
} catch (_: ApkFormatException) {
|
||||||
|
} catch (_: NoSuchAlgorithmException) {
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package app.revanced.patches.all.misc.spoof
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
|
||||||
import app.revanced.patcher.patch.stringOption
|
|
||||||
import app.revanced.util.getNode
|
|
||||||
import com.android.apksig.ApkVerifier
|
|
||||||
import com.android.apksig.apk.ApkFormatException
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.InvalidPathException
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.io.path.Path
|
|
||||||
|
|
||||||
val signatureSpoofPatch = resourcePatch(
|
|
||||||
name = "Spoof app signature",
|
|
||||||
description = "Spoofs the app signature via the \"fake-signature\" meta key. " +
|
|
||||||
"This patch only works with patched device roms.",
|
|
||||||
use = false,
|
|
||||||
) {
|
|
||||||
val signature by stringOption(
|
|
||||||
key = "spoofedAppSignature",
|
|
||||||
title = "Signature",
|
|
||||||
validator = { signature ->
|
|
||||||
optionToSignature(signature) != null
|
|
||||||
},
|
|
||||||
description = "The hex-encoded signature or path to an apk file with the desired signature",
|
|
||||||
required = true,
|
|
||||||
)
|
|
||||||
execute {
|
|
||||||
document("AndroidManifest.xml").use { document ->
|
|
||||||
val manifest = document.getNode("manifest") as Element
|
|
||||||
|
|
||||||
val fakeSignaturePermission = document.createElement("uses-permission")
|
|
||||||
fakeSignaturePermission.setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE")
|
|
||||||
manifest.appendChild(fakeSignaturePermission)
|
|
||||||
|
|
||||||
val application = document.getNode("application") ?: {
|
|
||||||
val child = document.createElement("application")
|
|
||||||
manifest.appendChild(child)
|
|
||||||
child
|
|
||||||
} as Element;
|
|
||||||
|
|
||||||
val fakeSignatureMetadata = document.createElement("meta-data")
|
|
||||||
fakeSignatureMetadata.setAttribute("android:name", "fake-signature")
|
|
||||||
fakeSignatureMetadata.setAttribute("android:value", optionToSignature(signature))
|
|
||||||
application.appendChild(fakeSignatureMetadata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun optionToSignature(signature: String?): String? {
|
|
||||||
if (signature == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// TODO: Replace with signature.hexToByteArray when stable in kotlin
|
|
||||||
val signatureBytes = HexFormat.of()
|
|
||||||
.parseHex(signature)
|
|
||||||
val factory = CertificateFactory.getInstance("X.509")
|
|
||||||
factory.generateCertificate(ByteArrayInputStream(signatureBytes))
|
|
||||||
return signature;
|
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
} catch (_: CertificateException) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
val signaturePath = Path(signature)
|
|
||||||
if (!Files.readAttributes(signaturePath, BasicFileAttributes::class.java).isRegularFile) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
val verifier = ApkVerifier.Builder(signaturePath.toFile())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val result = verifier.verify()
|
|
||||||
if (result.isVerifiedUsingV3Scheme) {
|
|
||||||
return HexFormat.of().formatHex(result.v3SchemeSigners[0].certificate.encoded)
|
|
||||||
} else if (result.isVerifiedUsingV2Scheme) {
|
|
||||||
return HexFormat.of().formatHex(result.v2SchemeSigners[0].certificate.encoded)
|
|
||||||
} else if (result.isVerifiedUsingV1Scheme) {
|
|
||||||
return HexFormat.of().formatHex(result.v1SchemeSigners[0].certificate.encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (_: IOException) {
|
|
||||||
} catch (_: InvalidPathException) {
|
|
||||||
} catch (_: ApkFormatException) {
|
|
||||||
} catch (_: NoSuchAlgorithmException) {
|
|
||||||
} catch (_: IllegalArgumentException) {}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -37,6 +37,20 @@ val spoofClientPatch = bytecodePatch(
|
|||||||
|
|
||||||
dependsOn(
|
dependsOn(
|
||||||
sharedExtensionPatch,
|
sharedExtensionPatch,
|
||||||
|
hexPatch(ignoreMissingTargetFiles = true, block = fun HexPatchBuilder.() {
|
||||||
|
listOf(
|
||||||
|
"arm64-v8a",
|
||||||
|
"armeabi-v7a",
|
||||||
|
"x86",
|
||||||
|
"x86_64"
|
||||||
|
).forEach { architecture ->
|
||||||
|
"https://login5.spotify.com/v3/login" to "http://127.0.0.1:$requestListenerPort/v3/login" inFile
|
||||||
|
"lib/$architecture/liborbit-jni-spotify.so"
|
||||||
|
|
||||||
|
"https://login5.spotify.com/v4/login" to "http://127.0.0.1:$requestListenerPort/v4/login" inFile
|
||||||
|
"lib/$architecture/liborbit-jni-spotify.so"
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith("com.spotify.music")
|
compatibleWith("com.spotify.music")
|
||||||
@@ -89,6 +103,82 @@ val spoofClientPatch = bytecodePatch(
|
|||||||
|
|
||||||
// region Spoof client.
|
// region Spoof client.
|
||||||
|
|
||||||
|
loadOrbitLibraryFingerprint.method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/16 v0, $requestListenerPort
|
||||||
|
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->launchListener(I)V
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
startupPageLayoutInflateFingerprint.method.apply {
|
||||||
|
val openLoginWebViewDescriptor =
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->launchLogin(Landroid/view/LayoutInflater;)V"
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
0,
|
||||||
|
"invoke-static/range { p1 .. p1 }, $openLoginWebViewDescriptor"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStartLoginScreenFingerprint.method.apply {
|
||||||
|
val onEventIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.INVOKE_INTERFACE && getReference<MethodReference>()?.name == "getView"
|
||||||
|
}
|
||||||
|
|
||||||
|
val buttonRegister = getInstruction<OneRegisterInstruction>(onEventIndex + 1).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
onEventIndex + 2,
|
||||||
|
"invoke-static { v$buttonRegister }, $EXTENSION_CLASS_DESCRIPTOR->setNativeLoginHandler(Landroid/view/View;)V"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSecondLoginScreenFingerprint.method.apply {
|
||||||
|
val getViewIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
opcode == Opcode.INVOKE_INTERFACE && getReference<MethodReference>()?.name == "getView"
|
||||||
|
}
|
||||||
|
|
||||||
|
val buttonRegister = getInstruction<OneRegisterInstruction>(getViewIndex + 1).registerA
|
||||||
|
|
||||||
|
// Early return the render for loop since the first item of the loop is the login button.
|
||||||
|
addInstructions(
|
||||||
|
getViewIndex + 2,
|
||||||
|
"""
|
||||||
|
invoke-virtual { v$buttonRegister }, Landroid/view/View;->performClick()Z
|
||||||
|
return-void
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderThirdLoginScreenFingerprint.method.apply {
|
||||||
|
val invokeSetListenerIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.definingClass == "Landroid/view/View;" && reference.name == "setOnClickListener"
|
||||||
|
}
|
||||||
|
|
||||||
|
val buttonRegister = getInstruction<FiveRegisterInstruction>(invokeSetListenerIndex).registerC
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
invokeSetListenerIndex + 1,
|
||||||
|
"invoke-virtual { v$buttonRegister }, Landroid/view/View;->performClick()Z"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
thirdLoginScreenLoginOnClickFingerprint.method.apply {
|
||||||
|
// Use placeholder credentials to pass the login screen.
|
||||||
|
val loginActionIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID) - 1
|
||||||
|
val loginActionInstruction = getInstruction<FiveRegisterInstruction>(loginActionIndex)
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
loginActionIndex,
|
||||||
|
"""
|
||||||
|
const-string v${loginActionInstruction.registerD}, "placeholder"
|
||||||
|
const-string v${loginActionInstruction.registerE}, "placeholder"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Disable verdicts.
|
// region Disable verdicts.
|
||||||
|
|||||||
Reference in New Issue
Block a user