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+ execute {
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