Compare commits

...

28 Commits

Author SHA1 Message Date
semantic-release-bot
83743929c8 chore(release): 1.9.1 [skip ci]
## [1.9.1](https://github.com/revanced/revanced-cli/compare/v1.9.0...v1.9.1) (2022-06-22)

### Bug Fixes

* add back in: option to specify keystore file path ([c94471f](c94471f464))
* remove logger from Signer.kt ([51e091c](51e091ce40))

### Reverts

* "feat: use of `java.util.logging.Logger`" ([2c8a106](2c8a106151))
2022-06-22 13:03:27 +00:00
Lucaskyy
51e091ce40 fix: remove logger from Signer.kt 2022-06-22 15:01:48 +02:00
Lucaskyy
e5a37e0a5f refactor: move signing logs 2022-06-22 15:00:24 +02:00
Lucaskyy
c94471f464 fix: add back in: option to specify keystore file path 2022-06-22 14:58:12 +02:00
Lucaskyy
bfd50a43b9 build: update patcher version 2022-06-22 14:57:33 +02:00
Lucaskyy
2c8a106151 revert: "feat: use of java.util.logging.Logger"
This reverts commit 07f6bdf330.
This reverts commit 6c4c1924ee.
2022-06-22 14:56:25 +02:00
semantic-release-bot
ea7efd2afc chore(release): 1.9.0 [skip ci]
# [1.9.0](https://github.com/revanced/revanced-cli/compare/v1.8.0...v1.9.0) (2022-06-22)

### Features

* migrate logger to `slf4j` ([6c4c192](6c4c1924ee))
2022-06-22 12:20:50 +00:00
oSumAtrIX
6c4c1924ee feat: migrate logger to slf4j 2022-06-22 14:19:06 +02:00
semantic-release-bot
ce78b245d1 chore(release): 1.8.0 [skip ci]
# [1.8.0](https://github.com/revanced/revanced-cli/compare/v1.7.1...v1.8.0) (2022-06-22)

### Features

* add option to specify keystore file path ([9331594](9331594706))
* use of `java.util.logging.Logger` ([07f6bdf](07f6bdf330))
2022-06-22 11:52:35 +00:00
oSumAtrIX
d7cffea99c build: bump patcher dependency version 2022-06-22 13:50:55 +02:00
oSumAtrIX
9331594706 feat: add option to specify keystore file path 2022-06-22 13:50:54 +02:00
oSumAtrIX
07f6bdf330 feat: use of java.util.logging.Logger 2022-06-22 13:50:54 +02:00
oSumAtrIX
a48c0860e3 refactor: simply if condition 2022-06-22 13:50:54 +02:00
semantic-release-bot
4d5efab8bd chore(release): 1.7.1 [skip ci]
## [1.7.1](https://github.com/revanced/revanced-cli/compare/v1.7.0...v1.7.1) (2022-06-22)

### Bug Fixes

* migrate to changes of patcher ([b30c737](b30c7375a7))
* wrong variable inverted ([f694542](f694542d64))
2022-06-22 10:08:28 +00:00
oSumAtrIX
b30c7375a7 fix: migrate to changes of patcher 2022-06-22 12:06:53 +02:00
Sculas
f694542d64 fix: wrong variable inverted 2022-06-22 11:49:15 +02:00
oSumAtrIX
6f54af5963 build: bump patcher dependency version 2022-06-22 02:57:43 +02:00
semantic-release-bot
0a52180431 chore(release): 1.7.0 [skip ci]
# [1.7.0](https://github.com/revanced/revanced-cli/compare/v1.6.3...v1.7.0) (2022-06-21)

### Features

* show description when listing patches ([af32572](af32572f29))
2022-06-21 22:05:10 +00:00
bogadana
af32572f29 feat: show description when listing patches 2022-06-22 00:03:48 +02:00
semantic-release-bot
e126436f3d chore(release): 1.6.3 [skip ci]
## [1.6.3](https://github.com/revanced/revanced-cli/compare/v1.6.2...v1.6.3) (2022-06-21)

### Bug Fixes

* update patcher version ([80c11fe](80c11fef73))
2022-06-21 22:02:17 +00:00
Sculas
80c11fef73 fix: update patcher version 2022-06-22 00:00:51 +02:00
semantic-release-bot
debf0116fb chore(release): 1.6.2 [skip ci]
## [1.6.2](https://github.com/revanced/revanced-cli/compare/v1.6.1...v1.6.2) (2022-06-21)

### Bug Fixes

* CLI not working ([29105ba](29105bab3d))
* improper use of mount variable ([31853fe](31853fe539))
2022-06-21 20:21:41 +00:00
Lucaskyy
29105bab3d fix: CLI not working 2022-06-21 22:20:08 +02:00
Lucaskyy
31853fe539 fix: improper use of mount variable 2022-06-21 22:19:34 +02:00
Lucaskyy
21747d5552 build: update patcher version 2022-06-21 22:18:35 +02:00
Lucaskyy
ee6aff8fe7 chore: add comment 2022-06-21 19:47:03 +02:00
Lucaskyy
f3a3e935a2 refactor: prevent any future regressions in zipfs 2022-06-21 19:31:49 +02:00
Lucaskyy
c272d55e2d chore: cleanup code 2022-06-21 19:30:24 +02:00
10 changed files with 142 additions and 49 deletions

View File

@@ -1,3 +1,61 @@
## [1.9.1](https://github.com/revanced/revanced-cli/compare/v1.9.0...v1.9.1) (2022-06-22)
### Bug Fixes
* add back in: option to specify keystore file path ([c94471f](https://github.com/revanced/revanced-cli/commit/c94471f4643e44b2b472ff0d826db0d2743bdc86))
* remove logger from Signer.kt ([51e091c](https://github.com/revanced/revanced-cli/commit/51e091ce4021418508044029aa5af6aa7d5162a3))
### Reverts
* "feat: use of `java.util.logging.Logger`" ([2c8a106](https://github.com/revanced/revanced-cli/commit/2c8a10615192635202ddc137fc02f175c5914d8f))
# [1.9.0](https://github.com/revanced/revanced-cli/compare/v1.8.0...v1.9.0) (2022-06-22)
### Features
* migrate logger to `slf4j` ([6c4c192](https://github.com/revanced/revanced-cli/commit/6c4c1924ee9ae75af3449749a6a82b7ae5572129))
# [1.8.0](https://github.com/revanced/revanced-cli/compare/v1.7.1...v1.8.0) (2022-06-22)
### Features
* add option to specify keystore file path ([9331594](https://github.com/revanced/revanced-cli/commit/9331594706404df871d170110da753cde5058d02))
* use of `java.util.logging.Logger` ([07f6bdf](https://github.com/revanced/revanced-cli/commit/07f6bdf33069da4e11fc40090feb726433de703e))
## [1.7.1](https://github.com/revanced/revanced-cli/compare/v1.7.0...v1.7.1) (2022-06-22)
### Bug Fixes
* migrate to changes of patcher ([b30c737](https://github.com/revanced/revanced-cli/commit/b30c7375a7ea61184be5dca19062ee74d1f97692))
* wrong variable inverted ([f694542](https://github.com/revanced/revanced-cli/commit/f694542d64ccb06bfa4d042f26b6b7192d1e912e))
# [1.7.0](https://github.com/revanced/revanced-cli/compare/v1.6.3...v1.7.0) (2022-06-21)
### Features
* show description when listing patches ([af32572](https://github.com/revanced/revanced-cli/commit/af32572f29d0f0a45ee5c3e01ba4cf1f91fe2f10))
## [1.6.3](https://github.com/revanced/revanced-cli/compare/v1.6.2...v1.6.3) (2022-06-21)
### Bug Fixes
* update patcher version ([80c11fe](https://github.com/revanced/revanced-cli/commit/80c11fef734bdba9026e91f95ee0a4a522cbefab))
## [1.6.2](https://github.com/revanced/revanced-cli/compare/v1.6.1...v1.6.2) (2022-06-21)
### Bug Fixes
* CLI not working ([29105ba](https://github.com/revanced/revanced-cli/commit/29105bab3dabd9d16af6b511caef9727f98afd1a))
* improper use of mount variable ([31853fe](https://github.com/revanced/revanced-cli/commit/31853fe5393af04805857b78a54434e1c51e4c8e))
## [1.6.1](https://github.com/revanced/revanced-cli/compare/v1.6.0...v1.6.1) (2022-06-21) ## [1.6.1](https://github.com/revanced/revanced-cli/compare/v1.6.0...v1.6.1) (2022-06-21)

View File

@@ -31,7 +31,8 @@ repositories {
dependencies { dependencies {
implementation(kotlin("stdlib")) implementation(kotlin("stdlib"))
implementation(kotlin("reflect")) implementation(kotlin("reflect"))
implementation("app.revanced:revanced-patcher:1.3.1")
implementation("app.revanced:revanced-patcher:1.7.0")
implementation("info.picocli:picocli:4.6.3") implementation("info.picocli:picocli:4.6.3")
implementation("com.android.tools.build:apksig:7.2.1") implementation("com.android.tools.build:apksig:7.2.1")
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 1.6.1 version = 1.9.1

View File

@@ -2,7 +2,9 @@ package app.revanced.cli.command
import app.revanced.cli.patcher.Patcher import app.revanced.cli.patcher.Patcher
import app.revanced.cli.signing.Signing import app.revanced.cli.signing.Signing
import app.revanced.cli.signing.SigningOptions
import app.revanced.patcher.PatcherOptions import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.util.patch.implementation.JarPatchBundle import app.revanced.patcher.util.patch.implementation.JarPatchBundle
import app.revanced.utils.adb.Adb import app.revanced.utils.adb.Adb
@@ -11,10 +13,9 @@ import java.io.File
import java.nio.file.Files import java.nio.file.Files
@Command( @Command(
name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true, name = "ReVanced-CLI", version = ["1.0.0"], mixinStandardHelpOptions = true
) )
internal object MainCommand : Runnable { internal object MainCommand : Runnable {
@ArgGroup(exclusive = false, multiplicity = "1") @ArgGroup(exclusive = false, multiplicity = "1")
lateinit var args: Args lateinit var args: Args
@@ -59,6 +60,9 @@ internal object MainCommand : Runnable {
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"]) @Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
var cn = "ReVanced" var cn = "ReVanced"
@Option(names = ["--keystore"], description = ["File path to your keystore"])
var keystorePath: String? = null
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"]) @Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
var password = "ReVanced" var password = "ReVanced"
@@ -78,7 +82,7 @@ internal object MainCommand : Runnable {
override fun run() { override fun run() {
if (args.lArgs?.listOnly == true) { if (args.lArgs?.listOnly == true) {
for (patchBundlePath in args.patchBundles) for (patch in JarPatchBundle(patchBundlePath).loadPatches()) { for (patchBundlePath in args.patchBundles) for (patch in JarPatchBundle(patchBundlePath).loadPatches()) {
println("[available] ${patch.patchName}") println("[available] ${patch.patchName}: ${patch.description}")
} }
return return
} }
@@ -97,19 +101,22 @@ internal object MainCommand : Runnable {
Adb(outputFile, patcher.data.packageMetadata.packageName, args.deploy!!, !args.mount) Adb(outputFile, patcher.data.packageMetadata.packageName, args.deploy!!, !args.mount)
} }
val patchedFile = val patchedFile = if (args.mount) outputFile
if (args.mount) outputFile else File(args.cacheDirectory).resolve("${outputFile.nameWithoutExtension}_raw.apk") else File(args.cacheDirectory).resolve("${outputFile.nameWithoutExtension}_raw.apk")
Patcher.start(patcher, patchedFile) Patcher.start(patcher, patchedFile)
println("[aligning & signing]") if (!args.mount) {
if (args.mount) {
Signing.start( Signing.start(
patchedFile, patchedFile,
outputFile, outputFile,
args.cn, SigningOptions(
args.password, args.cn,
args.password,
args.keystorePath ?: outputFile.parentFile
.resolve("${outputFile.nameWithoutExtension}.keystore")
.name
)
) )
} }

View File

@@ -10,7 +10,7 @@ import java.nio.file.Files
internal object Patcher { internal object Patcher {
internal fun start(patcher: app.revanced.patcher.Patcher, output: File) { internal fun start(patcher: app.revanced.patcher.Patcher, output: File) {
val args = args.pArgs!! val args = args.pArgs!!
// merge files like necessary integrations // merge files like necessary integrations
patcher.mergeFiles() patcher.mergeFiles()
@@ -23,16 +23,20 @@ internal object Patcher {
if (output.exists()) Files.delete(output.toPath()) if (output.exists()) Files.delete(output.toPath())
args.inputFile.copyTo(output) args.inputFile.copyTo(output)
ZipFileSystemUtils(output).use { fileSystem -> val result = patcher.save()
val inputFile = if (!args.disableResourcePatching && result.resourceFile != null) {
result.resourceFile
} else null
ZipFileSystemUtils(inputFile, output).use { fileSystem ->
// replace all dex files // replace all dex files
val result = patcher.save()
result.dexFiles.forEach { result.dexFiles.forEach {
fileSystem.write(it.name, it.memoryDataStore.data) fileSystem.write(it.name, it.dexFileInputStream.readAllBytes())
} }
// write resources // inputFile being null implies resource patching being disabled
if (!args.disableResourcePatching) { if (inputFile != null) {
fileSystem.writePathRecursively(File(args.cacheDirectory).resolve("build").toPath()) // write resources
fileSystem.writeInput()
fileSystem.uncompress(*result.doNotCompress!!.toTypedArray()) fileSystem.uncompress(*result.doNotCompress!!.toTypedArray())
} }
} }

View File

@@ -6,17 +6,19 @@ import app.revanced.utils.signing.align.ZipAligner
import java.io.File import java.io.File
object Signing { object Signing {
fun start(inputFile: File, outputFile: File, cn: String, password: String) { fun start(inputFile: File, outputFile: File, signingOptions: SigningOptions) {
val cacheDirectory = File(args.pArgs!!.cacheDirectory) val cacheDirectory = File(args.pArgs!!.cacheDirectory)
val alignedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk") val alignedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk") val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
// align the inputFile and write to alignedOutput // align the inputFile and write to alignedOutput
println("[aligning]")
ZipAligner.align(inputFile, alignedOutput) ZipAligner.align(inputFile, alignedOutput)
// sign the alignedOutput and write to signedOutput // sign the alignedOutput and write to signedOutput
// the reason is, in case the signer fails // the reason is, in case the signer fails
// it does not damage the output file // it does not damage the output file
val keyStore = Signer(cn, password).signApk(alignedOutput, signedOutput) println("[signing]")
val keyStore = Signer(signingOptions).signApk(alignedOutput, signedOutput)
// afterwards copy over the file and the keystore to the output // afterwards copy over the file and the keystore to the output
signedOutput.copyTo(outputFile, true) signedOutput.copyTo(outputFile, true)

View File

@@ -0,0 +1,7 @@
package app.revanced.cli.signing
data class SigningOptions(
val cn: String,
val password: String,
val keyStoreFilePath: String
)

View File

@@ -7,12 +7,17 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
internal class ZipFileSystemUtils( internal class ZipFileSystemUtils(input: File?, output: File) : Closeable {
file: File private val inFileSystem = if (input != null) {
) : Closeable { FileSystems.newFileSystem(input.toPath())
private var zipFileSystem = FileSystems.newFileSystem(file.toPath(), mapOf("noCompression" to true)) } else null
private val outFileSystem = FileSystems.newFileSystem(output.toPath(), mapOf("noCompression" to true))
private fun Path.deleteRecursively() { private fun Path.deleteRecursively() {
if (!Files.exists(this)) {
throw IllegalStateException("File exists in input but not in output, cannot delete")
}
if (Files.isDirectory(this)) { if (Files.isDirectory(this)) {
Files.list(this).forEach { path -> Files.list(this).forEach { path ->
path.deleteRecursively() path.deleteRecursively()
@@ -22,19 +27,25 @@ internal class ZipFileSystemUtils(
Files.delete(this) Files.delete(this)
} }
internal fun writePathRecursively(path: Path) { internal fun writeInput() {
Files.list(path).let { fileStream -> if (inFileSystem == null) {
throw IllegalArgumentException("Input file not set")
}
val root = inFileSystem.getPath(inFileSystem.separator)
Files.list(root).close()
Files.list(root).also { fileStream ->
fileStream.forEach { filePath -> fileStream.forEach { filePath ->
val fileSystemPath = filePath.getRelativePath(path) val fileSystemPath = filePath.getRelativePath(root)
fileSystemPath.deleteRecursively() fileSystemPath.deleteRecursively()
} }
fileStream
}.close() }.close()
Files.walk(path).let { fileStream -> Files.walk(root).also { fileStream ->
// don't include build directory by skipping the root node.
fileStream.skip(1).forEach { filePath -> fileStream.skip(1).forEach { filePath ->
val relativePath = filePath.getRelativePath(path) val relativePath = filePath.getRelativePath(root)
if (Files.isDirectory(filePath)) { if (Files.isDirectory(filePath)) {
Files.createDirectory(relativePath) Files.createDirectory(relativePath)
@@ -43,17 +54,18 @@ internal class ZipFileSystemUtils(
Files.copy(filePath, relativePath) Files.copy(filePath, relativePath)
} }
fileStream
}.close() }.close()
} }
internal fun write(path: String, content: ByteArray) = Files.write(zipFileSystem.getPath(path), content) internal fun write(path: String, content: ByteArray) = Files.write(outFileSystem.getPath(path), content)
private fun Path.getRelativePath(path: Path): Path = zipFileSystem.getPath(path.relativize(this).toString()) private fun Path.getRelativePath(path: Path): Path = outFileSystem.getPath(path.relativize(this).toString())
internal fun uncompress(vararg paths: String) = internal fun uncompress(vararg paths: String) =
paths.forEach { Files.setAttribute(zipFileSystem.getPath(it), "zip:method", ZipEntry.STORED) } paths.forEach { Files.setAttribute(outFileSystem.getPath(it), "zip:method", ZipEntry.STORED) }
override fun close() = zipFileSystem.close() override fun close() {
inFileSystem?.close()
outFileSystem.close()
}
} }

View File

@@ -1,7 +1,6 @@
package app.revanced.utils.signature package app.revanced.utils.signature
import app.revanced.patcher.Patcher import app.revanced.patcher.Patcher
import org.jf.dexlib2.iface.Method
object Signature { object Signature {
@@ -49,7 +48,7 @@ object Signature {
*/ */
} }
private fun Method.toStr(): String { //private fun Method.toStr(): String {
return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}" // return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}"
} //}
} }

View File

@@ -1,5 +1,6 @@
package app.revanced.utils.signing package app.revanced.utils.signing
import app.revanced.cli.signing.SigningOptions
import com.android.apksig.ApkSigner import com.android.apksig.ApkSigner
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
@@ -17,9 +18,9 @@ import java.security.cert.X509Certificate
import java.util.* import java.util.*
internal class Signer( internal class Signer(
private val cn: String, password: String private val signingOptions: SigningOptions
) { ) {
private val passwordCharArray = password.toCharArray() private val passwordCharArray = signingOptions.password.toCharArray()
private fun newKeystore(out: File) { private fun newKeystore(out: File) {
val (publicKey, privateKey) = createKey() val (publicKey, privateKey) = createKey()
val privateKS = KeyStore.getInstance("BKS", "BC") val privateKS = KeyStore.getInstance("BKS", "BC")
@@ -34,7 +35,7 @@ internal class Signer(
val pair = gen.generateKeyPair() val pair = gen.generateKeyPair()
var serialNumber: BigInteger var serialNumber: BigInteger
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO) do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
val x500Name = X500Name("CN=$cn") val x500Name = X500Name("CN=${signingOptions.cn}")
val builder = X509v3CertificateBuilder( val builder = X509v3CertificateBuilder(
x500Name, x500Name,
serialNumber, serialNumber,
@@ -52,21 +53,23 @@ internal class Signer(
Security.addProvider(BouncyCastleProvider()) Security.addProvider(BouncyCastleProvider())
// TODO: keystore should be saved securely // TODO: keystore should be saved securely
val ks = File(input.parent, "${output.nameWithoutExtension}.keystore") val ks = File(signingOptions.keyStoreFilePath)
if (!ks.exists()) newKeystore(ks) if (!ks.exists()) newKeystore(ks) else {
println("found existing keystore: ${ks.nameWithoutExtension}")
}
val keyStore = KeyStore.getInstance("BKS", "BC") val keyStore = KeyStore.getInstance("BKS", "BC")
FileInputStream(ks).use { fis -> keyStore.load(fis, null) } FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
val alias = keyStore.aliases().nextElement() val alias = keyStore.aliases().nextElement()
val config = ApkSigner.SignerConfig.Builder( val config = ApkSigner.SignerConfig.Builder(
cn, signingOptions.cn,
keyStore.getKey(alias, passwordCharArray) as PrivateKey, keyStore.getKey(alias, passwordCharArray) as PrivateKey,
listOf(keyStore.getCertificate(alias) as X509Certificate) listOf(keyStore.getCertificate(alias) as X509Certificate)
).build() ).build()
val signer = ApkSigner.Builder(listOf(config)) val signer = ApkSigner.Builder(listOf(config))
signer.setCreatedBy(cn) signer.setCreatedBy(signingOptions.cn)
signer.setInputApk(input) signer.setInputApk(input)
signer.setOutputApk(output) signer.setOutputApk(output)