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.ByteArrayInputStream
10
+ import java.io.IOException
11
+ import java.nio.file.Files
12
+ import java.nio.file.InvalidPathException
13
+ import java.nio.file.attribute.BasicFileAttributes
14
+ import java.security.NoSuchAlgorithmException
15
+ import java.security.cert.CertificateException
16
+ import java.security.cert.CertificateFactory
17
+ import java.util.*
18
+ import kotlin.io.path.Path
19
+
20
+ val signatureSpoofPatch = resourcePatch(
21
+ name = " Spoof app signature" ,
22
+ description = " Spoofs the app signature via the \" fake-signature\" meta key. " +
23
+ " This patch only works with patched device roms." ,
24
+ use = false ,
25
+ ) {
26
+ val signature by stringOption(
27
+ key = " spoofedAppSignature" ,
28
+ title = " Signature" ,
29
+ validator = { signature ->
30
+ optionToSignature(signature) != null
31
+ },
32
+ description = " The hex-encoded signature or path to an apk file with the desired signature" ,
33
+ required = true ,
34
+ )
35
+ finalize {
36
+ document(" AndroidManifest.xml" ).use { document ->
37
+ val manifest = document.getNode(" manifest" ) as Element
38
+
39
+ val fakeSignaturePermission = document.createElement(" uses-permission" )
40
+ fakeSignaturePermission.setAttribute(" android:name" , " android.permission.FAKE_PACKAGE_SIGNATURE" )
41
+ manifest.appendChild(fakeSignaturePermission)
42
+
43
+ val application = document.getNode(" application" ) ? : {
44
+ val child = document.createElement(" application" )
45
+ manifest.appendChild(child)
46
+ child
47
+ } as Element ;
48
+
49
+ val fakeSignatureMetadata = document.createElement(" meta-data" )
50
+ fakeSignatureMetadata.setAttribute(" android:name" , " fake-signature" )
51
+ fakeSignatureMetadata.setAttribute(" android:value" , optionToSignature(signature))
52
+ application.appendChild(fakeSignatureMetadata)
53
+ }
54
+ }
55
+ }
56
+
57
+ internal fun optionToSignature (signature : String? ): String? {
58
+ if (signature == null ) {
59
+ return null ;
60
+ }
61
+ try {
62
+ // TODO: Replace with signature.hexToByteArray when stable in kotlin
63
+ val signatureBytes = HexFormat .of()
64
+ .parseHex(signature)
65
+ val factory = CertificateFactory .getInstance(" X.509" )
66
+ factory.generateCertificate(ByteArrayInputStream (signatureBytes))
67
+ return signature;
68
+ } catch (_: IllegalArgumentException ) {
69
+ } catch (_: CertificateException ) {
70
+ }
71
+ try {
72
+ val signaturePath = Path (signature)
73
+ if (! Files .readAttributes(signaturePath, BasicFileAttributes ::class .java).isRegularFile) {
74
+ return null ;
75
+ }
76
+ val verifier = ApkVerifier .Builder (signaturePath.toFile())
77
+ .build()
78
+
79
+ val result = verifier.verify()
80
+ if (result.isVerifiedUsingV3Scheme) {
81
+ return HexFormat .of().formatHex(result.v3SchemeSigners[0 ].certificate.encoded)
82
+ } else if (result.isVerifiedUsingV2Scheme) {
83
+ return HexFormat .of().formatHex(result.v2SchemeSigners[0 ].certificate.encoded)
84
+ } else if (result.isVerifiedUsingV1Scheme) {
85
+ return HexFormat .of().formatHex(result.v1SchemeSigners[0 ].certificate.encoded)
86
+ }
87
+
88
+ return null ;
89
+ } catch (_: IOException ) {
90
+ } catch (_: InvalidPathException ) {
91
+ } catch (_: ApkFormatException ) {
92
+ } catch (_: NoSuchAlgorithmException ) {
93
+ } catch (_: IllegalArgumentException ) {}
94
+ return null ;
95
+ }
0 commit comments