mirror of
https://github.com/ReVanced/revanced-cli.git
synced 2026-01-28 21:51:06 +00:00
feat: use separate command to patch
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package app.revanced.utils.signing.align
|
||||
package app.revanced.utils.align
|
||||
|
||||
import app.revanced.utils.signing.align.zip.structures.ZipEntry
|
||||
import app.revanced.utils.align.zip.structures.ZipEntry
|
||||
|
||||
internal object ZipAligner {
|
||||
private const val DEFAULT_ALIGNMENT = 4
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.utils.signing.align.zip
|
||||
package app.revanced.utils.align.zip
|
||||
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
@@ -1,7 +1,7 @@
|
||||
package app.revanced.utils.signing.align.zip
|
||||
package app.revanced.utils.align.zip
|
||||
|
||||
import app.revanced.utils.signing.align.zip.structures.ZipEndRecord
|
||||
import app.revanced.utils.signing.align.zip.structures.ZipEntry
|
||||
import app.revanced.utils.align.zip.structures.ZipEndRecord
|
||||
import app.revanced.utils.align.zip.structures.ZipEntry
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.RandomAccessFile
|
||||
@@ -11,15 +11,15 @@ import java.util.zip.CRC32
|
||||
import java.util.zip.Deflater
|
||||
|
||||
class ZipFile(file: File) : Closeable {
|
||||
var entries: MutableList<ZipEntry> = mutableListOf()
|
||||
private var entries: MutableList<ZipEntry> = mutableListOf()
|
||||
|
||||
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
|
||||
private var CDNeedsRewrite = false
|
||||
private var centralDirectoryNeedsRewrite = false
|
||||
|
||||
private val compressionLevel = 5
|
||||
|
||||
init {
|
||||
//if file isn't empty try to load entries
|
||||
// If file isn't empty try to load entries.
|
||||
if (file.length() > 0) {
|
||||
val endRecord = findEndRecord()
|
||||
|
||||
@@ -29,17 +29,17 @@ class ZipFile(file: File) : Closeable {
|
||||
entries = readEntries(endRecord).toMutableList()
|
||||
}
|
||||
|
||||
//seek back to start for writing
|
||||
// Seek back to start for writing.
|
||||
filePointer.seek(0)
|
||||
}
|
||||
|
||||
private fun findEndRecord(): ZipEndRecord {
|
||||
//look from end to start since end record is at the end
|
||||
// Look from end to start since end record is at the end.
|
||||
for (i in filePointer.length() - 1 downTo 0) {
|
||||
filePointer.seek(i)
|
||||
//possible beginning of signature
|
||||
// Possible beginning of signature.
|
||||
if (filePointer.readByte() == 0x50.toByte()) {
|
||||
//seek back to get the full int
|
||||
// Seek back to get the full int.
|
||||
filePointer.seek(i)
|
||||
val possibleSignature = filePointer.readUIntLE()
|
||||
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
|
||||
@@ -76,7 +76,7 @@ class ZipFile(file: File) : Closeable {
|
||||
}
|
||||
|
||||
private fun writeCD() {
|
||||
val CDStart = filePointer.channel.position().toUInt()
|
||||
val centralDirectoryStartOffset = filePointer.channel.position().toUInt()
|
||||
|
||||
entries.forEach {
|
||||
filePointer.channel.write(it.toCDE())
|
||||
@@ -89,8 +89,8 @@ class ZipFile(file: File) : Closeable {
|
||||
0u,
|
||||
entriesCount,
|
||||
entriesCount,
|
||||
filePointer.channel.position().toUInt() - CDStart,
|
||||
CDStart,
|
||||
filePointer.channel.position().toUInt() - centralDirectoryStartOffset,
|
||||
centralDirectoryStartOffset,
|
||||
""
|
||||
)
|
||||
|
||||
@@ -98,7 +98,7 @@ class ZipFile(file: File) : Closeable {
|
||||
}
|
||||
|
||||
private fun addEntry(entry: ZipEntry, data: ByteBuffer) {
|
||||
CDNeedsRewrite = true
|
||||
centralDirectoryNeedsRewrite = true
|
||||
|
||||
entry.localHeaderOffset = filePointer.channel.position().toUInt()
|
||||
|
||||
@@ -114,8 +114,7 @@ class ZipFile(file: File) : Closeable {
|
||||
compressor.finish()
|
||||
|
||||
val uncompressedSize = data.size
|
||||
val compressedData =
|
||||
ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger
|
||||
val compressedData = ByteArray(uncompressedSize) // I'm guessing compression won't make the data bigger.
|
||||
|
||||
val compressedDataLength = compressor.deflate(compressedData)
|
||||
val compressedBuffer =
|
||||
@@ -126,7 +125,7 @@ class ZipFile(file: File) : Closeable {
|
||||
val crc = CRC32()
|
||||
crc.update(data)
|
||||
|
||||
entry.compression = 8u //deflate compression
|
||||
entry.compression = 8u // Deflate compression.
|
||||
entry.uncompressedSize = uncompressedSize.toUInt()
|
||||
entry.compressedSize = compressedDataLength.toUInt()
|
||||
entry.crc32 = crc.value.toUInt()
|
||||
@@ -136,14 +135,14 @@ class ZipFile(file: File) : Closeable {
|
||||
|
||||
private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
|
||||
alignment?.let {
|
||||
//calculate where data would end up
|
||||
// Calculate where data would end up.
|
||||
val dataOffset = filePointer.filePointer + entry.LFHSize
|
||||
|
||||
val mod = dataOffset % alignment
|
||||
|
||||
//wrong alignment
|
||||
// Wrong alignment.
|
||||
if (mod != 0L) {
|
||||
//add padding at end of extra field
|
||||
// Add padding at end of extra field.
|
||||
entry.localExtraField =
|
||||
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
|
||||
}
|
||||
@@ -152,7 +151,7 @@ class ZipFile(file: File) : Closeable {
|
||||
addEntry(entry, data)
|
||||
}
|
||||
|
||||
fun getDataForEntry(entry: ZipEntry): ByteBuffer {
|
||||
private fun getDataForEntry(entry: ZipEntry): ByteBuffer {
|
||||
return filePointer.channel.map(
|
||||
FileChannel.MapMode.READ_ONLY,
|
||||
entry.dataOffset.toLong(),
|
||||
@@ -160,9 +159,15 @@ class ZipFile(file: File) : Closeable {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all entries from [file] to this file but skip already existing entries.
|
||||
*
|
||||
* @param file The file to copy entries from.
|
||||
* @param entryAlignment A function that returns the alignment for a given entry.
|
||||
*/
|
||||
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) {
|
||||
for (entry in file.entries) {
|
||||
if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates
|
||||
if (entries.any { it.fileName == entry.fileName }) continue // Skip duplicates
|
||||
|
||||
val data = file.getDataForEntry(entry)
|
||||
addEntryCopyData(entry, data, entryAlignment(entry))
|
||||
@@ -170,7 +175,7 @@ class ZipFile(file: File) : Closeable {
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (CDNeedsRewrite) writeCD()
|
||||
if (centralDirectoryNeedsRewrite) writeCD()
|
||||
filePointer.close()
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package app.revanced.utils.signing.align.zip.structures
|
||||
package app.revanced.utils.align.zip.structures
|
||||
|
||||
import app.revanced.utils.signing.align.zip.putUInt
|
||||
import app.revanced.utils.signing.align.zip.putUShort
|
||||
import app.revanced.utils.signing.align.zip.readUIntLE
|
||||
import app.revanced.utils.signing.align.zip.readUShortLE
|
||||
import app.revanced.utils.align.zip.putUInt
|
||||
import app.revanced.utils.align.zip.putUShort
|
||||
import app.revanced.utils.align.zip.readUIntLE
|
||||
import app.revanced.utils.align.zip.readUShortLE
|
||||
import java.io.DataInput
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
@@ -1,6 +1,6 @@
|
||||
package app.revanced.utils.signing.align.zip.structures
|
||||
package app.revanced.utils.align.zip.structures
|
||||
|
||||
import app.revanced.utils.signing.align.zip.*
|
||||
import app.revanced.utils.align.zip.*
|
||||
import java.io.DataInput
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
@@ -1,7 +1,6 @@
|
||||
package app.revanced.utils.signing
|
||||
|
||||
import app.revanced.cli.command.MainCommand.logger
|
||||
import app.revanced.cli.signing.SigningOptions
|
||||
import app.revanced.cli.command.logger
|
||||
import com.android.apksig.ApkSigner
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||
@@ -18,10 +17,40 @@ import java.security.*
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
|
||||
internal class Signer(
|
||||
internal class ApkSigner(
|
||||
private val signingOptions: SigningOptions
|
||||
) {
|
||||
private val signer: ApkSigner.Builder
|
||||
private val passwordCharArray = signingOptions.password.toCharArray()
|
||||
|
||||
init {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
|
||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
||||
val alias = keyStore.let { store ->
|
||||
FileInputStream(File(signingOptions.keyStoreFilePath).also {
|
||||
if (!it.exists()) {
|
||||
logger.info("Creating keystore at ${it.absolutePath}")
|
||||
newKeystore(it)
|
||||
} else {
|
||||
logger.info("Using keystore at ${it.absolutePath}")
|
||||
}
|
||||
}).use { fis -> store.load(fis, null) }
|
||||
store.aliases().nextElement()
|
||||
}
|
||||
|
||||
with(
|
||||
ApkSigner.SignerConfig.Builder(
|
||||
signingOptions.cn,
|
||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
||||
).build()
|
||||
) {
|
||||
this@ApkSigner.signer = ApkSigner.Builder(listOf(this))
|
||||
signer.setCreatedBy(signingOptions.cn)
|
||||
}
|
||||
}
|
||||
|
||||
private fun newKeystore(out: File) {
|
||||
val (publicKey, privateKey) = createKey()
|
||||
val privateKS = KeyStore.getInstance("BKS", "BC")
|
||||
@@ -50,30 +79,12 @@ internal class Signer(
|
||||
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
|
||||
}
|
||||
|
||||
fun signApk(input: File, output: File) {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
|
||||
// TODO: keystore should be saved securely
|
||||
val ks = File(signingOptions.keyStoreFilePath)
|
||||
if (!ks.exists()) newKeystore(ks) else {
|
||||
logger.info("Found existing keystore: ${ks.name}")
|
||||
}
|
||||
|
||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
||||
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
|
||||
val alias = keyStore.aliases().nextElement()
|
||||
|
||||
val config = ApkSigner.SignerConfig.Builder(
|
||||
signingOptions.cn,
|
||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
||||
).build()
|
||||
|
||||
val signer = ApkSigner.Builder(listOf(config))
|
||||
signer.setCreatedBy(signingOptions.cn)
|
||||
fun signApk(input: File, output: File): File {
|
||||
signer.setInputApk(input)
|
||||
signer.setOutputApk(output)
|
||||
|
||||
signer.build().sign()
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package app.revanced.utils.signing
|
||||
|
||||
data class SigningOptions(
|
||||
val cn: String,
|
||||
val password: String,
|
||||
val keyStoreFilePath: String
|
||||
)
|
||||
Reference in New Issue
Block a user