Skip to content

Commit e2ac841

Browse files
committed
fix: Fix accidental changes
1 parent 61f5a9e commit e2ac841

File tree

3 files changed

+182
-95
lines changed

3 files changed

+182
-95
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package app.revanced.patches.all.misc.spoof
2+
3+
import app.revanced.patcher.patch.resourcePatch
4+
import app.revanced.patcher.patch.stringOption
5+
import app.revanced.util.getNode
6+
import com.android.apksig.ApkVerifier
7+
import com.android.apksig.apk.ApkFormatException
8+
import org.w3c.dom.Element
9+
import java.io.File
10+
import java.io.IOException
11+
import java.nio.file.InvalidPathException
12+
import java.security.NoSuchAlgorithmException
13+
import java.security.cert.CertificateException
14+
import java.security.cert.CertificateFactory
15+
import java.util.*
16+
17+
@Suppress("unused")
18+
val enableRomSignatureSpoofing = resourcePatch(
19+
name = "Enable ROM signature spoofing",
20+
description = "Spoofs the signature via the manifest meta-data \"fake-signature\". " +
21+
"This patch only works with ROMs that support signature spoofing.",
22+
use = false,
23+
) {
24+
val signatureOrPath by stringOption(
25+
key = "signatureOrApkFilePath",
26+
title = "Signature or APK file path",
27+
validator = validator@{ signature ->
28+
signature ?: return@validator false
29+
30+
parseSignature(signature) != null
31+
},
32+
description = "The hex-encoded signature or path to an APK file with the desired signature.",
33+
required = true,
34+
)
35+
execute {
36+
document("AndroidManifest.xml").use { document ->
37+
val permission = document.createElement("uses-permission").apply {
38+
setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE")
39+
}
40+
val manifest = document.getNode("manifest").appendChild(permission)
41+
42+
43+
val fakeSignatureMetadata = document.createElement("meta-data").apply {
44+
setAttribute("android:name", "fake-signature")
45+
setAttribute("android:value", parseSignature(signatureOrPath!!))
46+
}
47+
document.getNode("application").appendChild(fakeSignatureMetadata)
48+
}
49+
}
50+
}
51+
52+
private fun parseSignature(optionValue: String): String? {
53+
// Parse as a hex-encoded signature.
54+
try {
55+
// TODO: Replace with signature.hexToByteArray when stable in kotlin
56+
val signatureBytes = HexFormat.of().parseHex(optionValue)
57+
CertificateFactory.getInstance("X.509").generateCertificate(signatureBytes.inputStream())
58+
59+
return optionValue
60+
} catch (_: IllegalArgumentException) {
61+
} catch (_: CertificateException) {
62+
}
63+
64+
// Parse as a path to an APK file.
65+
try {
66+
val apkFile = File(optionValue)
67+
if (!apkFile.isFile) return null
68+
69+
val result = ApkVerifier.Builder(apkFile).build().verify()
70+
71+
val hexFormat = HexFormat.of()
72+
73+
val signature = (if (result.isVerifiedUsingV3Scheme) {
74+
result.v3SchemeSigners[0].certificate
75+
} else if (result.isVerifiedUsingV2Scheme) {
76+
result.v2SchemeSigners[0].certificate
77+
} else if (result.isVerifiedUsingV1Scheme) {
78+
result.v1SchemeSigners[0].certificate
79+
} else {
80+
return null
81+
}).encoded
82+
83+
return hexFormat.formatHex(signature)
84+
} catch (_: IOException) {
85+
} catch (_: InvalidPathException) {
86+
} catch (_: ApkFormatException) {
87+
} catch (_: NoSuchAlgorithmException) {
88+
} catch (_: IllegalArgumentException) {
89+
}
90+
91+
return null
92+
}

patches/src/main/kotlin/app/revanced/patches/all/misc/spoof/SignatureSpoofPatch.kt

Lines changed: 0 additions & 95 deletions
This file was deleted.

patches/src/main/kotlin/app/revanced/patches/spotify/misc/fix/SpoofClientPatch.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ val spoofClientPatch = bytecodePatch(
3737

3838
dependsOn(
3939
sharedExtensionPatch,
40+
hexPatch(ignoreMissingTargetFiles = true, block = fun HexPatchBuilder.() {
41+
listOf(
42+
"arm64-v8a",
43+
"armeabi-v7a",
44+
"x86",
45+
"x86_64"
46+
).forEach { architecture ->
47+
"https://login5.spotify.com/v3/login" to "http://127.0.0.1:$requestListenerPort/v3/login" inFile
48+
"lib/$architecture/liborbit-jni-spotify.so"
49+
50+
"https://login5.spotify.com/v4/login" to "http://127.0.0.1:$requestListenerPort/v4/login" inFile
51+
"lib/$architecture/liborbit-jni-spotify.so"
52+
}
53+
})
4054
)
4155

4256
compatibleWith("com.spotify.music")
@@ -89,6 +103,82 @@ val spoofClientPatch = bytecodePatch(
89103

90104
// region Spoof client.
91105

106+
loadOrbitLibraryFingerprint.method.addInstructions(
107+
0,
108+
"""
109+
const/16 v0, $requestListenerPort
110+
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->launchListener(I)V
111+
"""
112+
)
113+
114+
startupPageLayoutInflateFingerprint.method.apply {
115+
val openLoginWebViewDescriptor =
116+
"$EXTENSION_CLASS_DESCRIPTOR->launchLogin(Landroid/view/LayoutInflater;)V"
117+
118+
addInstructions(
119+
0,
120+
"invoke-static/range { p1 .. p1 }, $openLoginWebViewDescriptor"
121+
)
122+
}
123+
124+
renderStartLoginScreenFingerprint.method.apply {
125+
val onEventIndex = indexOfFirstInstructionOrThrow {
126+
opcode == Opcode.INVOKE_INTERFACE && getReference<MethodReference>()?.name == "getView"
127+
}
128+
129+
val buttonRegister = getInstruction<OneRegisterInstruction>(onEventIndex + 1).registerA
130+
131+
addInstruction(
132+
onEventIndex + 2,
133+
"invoke-static { v$buttonRegister }, $EXTENSION_CLASS_DESCRIPTOR->setNativeLoginHandler(Landroid/view/View;)V"
134+
)
135+
}
136+
137+
renderSecondLoginScreenFingerprint.method.apply {
138+
val getViewIndex = indexOfFirstInstructionOrThrow {
139+
opcode == Opcode.INVOKE_INTERFACE && getReference<MethodReference>()?.name == "getView"
140+
}
141+
142+
val buttonRegister = getInstruction<OneRegisterInstruction>(getViewIndex + 1).registerA
143+
144+
// Early return the render for loop since the first item of the loop is the login button.
145+
addInstructions(
146+
getViewIndex + 2,
147+
"""
148+
invoke-virtual { v$buttonRegister }, Landroid/view/View;->performClick()Z
149+
return-void
150+
"""
151+
)
152+
}
153+
154+
renderThirdLoginScreenFingerprint.method.apply {
155+
val invokeSetListenerIndex = indexOfFirstInstructionOrThrow {
156+
val reference = getReference<MethodReference>()
157+
reference?.definingClass == "Landroid/view/View;" && reference.name == "setOnClickListener"
158+
}
159+
160+
val buttonRegister = getInstruction<FiveRegisterInstruction>(invokeSetListenerIndex).registerC
161+
162+
addInstruction(
163+
invokeSetListenerIndex + 1,
164+
"invoke-virtual { v$buttonRegister }, Landroid/view/View;->performClick()Z"
165+
)
166+
}
167+
168+
thirdLoginScreenLoginOnClickFingerprint.method.apply {
169+
// Use placeholder credentials to pass the login screen.
170+
val loginActionIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID) - 1
171+
val loginActionInstruction = getInstruction<FiveRegisterInstruction>(loginActionIndex)
172+
173+
addInstructions(
174+
loginActionIndex,
175+
"""
176+
const-string v${loginActionInstruction.registerD}, "placeholder"
177+
const-string v${loginActionInstruction.registerE}, "placeholder"
178+
"""
179+
)
180+
}
181+
92182
// endregion
93183

94184
// region Disable verdicts.

0 commit comments

Comments
 (0)