Compare commits

...

20 Commits

Author SHA1 Message Date
semantic-release-bot
04e0027c08 chore(release): 1.1.4 [skip ci]
## [1.1.4](https://github.com/revanced/revanced-cli/compare/v1.1.3...v1.1.4) (2022-05-26)

### Bug Fixes

* migrate from `PatchLoader.load(...)` to `JarPatchBundle(...).loadPatches()` ([cabd32f](cabd32fda4))
2022-05-26 02:26:10 +00:00
oSumAtrIX
e76983e01c chore: update gradlew wrapper 2022-05-26 04:08:29 +02:00
oSumAtrIX
11d67bc1ea chore: update dependencies 2022-05-26 04:08:15 +02:00
oSumAtrIX
cabd32fda4 fix: migrate from PatchLoader.load(...) to JarPatchBundle(...).loadPatches()
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-26 01:02:42 +02:00
semantic-release-bot
61235d7c41 chore(release): 1.1.3 [skip ci]
## [1.1.3](https://github.com/revanced/revanced-cli/compare/v1.1.2...v1.1.3) (2022-05-25)

### Bug Fixes

* only accept directories when looking for files in resource patch ([c76da7e](c76da7e5ff))
2022-05-25 22:42:14 +00:00
oSumAtrIX
817154dd66 Merge pull request #18 from danthe1st/main
fix: only accept directories when looking for files in resource patch
2022-05-26 00:40:39 +02:00
danthe1st
c76da7e5ff fix: only accept directories when looking for files in resource patch 2022-05-26 00:31:29 +02:00
oSumAtrIX
d946333c96 Merge remote-tracking branch 'origin/main' 2022-05-24 02:10:15 +02:00
oSumAtrIX
97a036bfbc chore: update kotlin jvm
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-24 00:17:02 +02:00
oSumAtrIX
153d8ce746 chore: update dependencies
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-23 20:56:22 +02:00
oSumAtrIX
5cd3ea291c refactor: remove unused Patches class
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-23 20:35:03 +02:00
semantic-release-bot
c8a5e97f0d chore(release): 1.1.2 [skip ci]
## [1.1.2](https://github.com/revanced/revanced-cli/compare/v1.1.1...v1.1.2) (2022-05-22)

### Bug Fixes

* delete `outputFile` after deploying ([329f8a3](329f8a383f))
2022-05-22 20:39:34 +00:00
oSumAtrIX
329f8a383f fix: delete outputFile after deploying 2022-05-22 22:38:27 +02:00
semantic-release-bot
fb1cd41521 chore(release): 1.1.1 [skip ci]
## [1.1.1](https://github.com/revanced/revanced-cli/compare/v1.1.0...v1.1.1) (2022-05-22)

### Bug Fixes

* breaking changes by `revanced-patcher` dependency ([51d2504](51d250491f))
* wrong use of dependency to `revanced-patches` ([351de6c](351de6cb90))
* wrong use of variable substitution / typo ([81d53b5](81d53b5518)), closes [revanced/revanced-cli#12](https://github.com/revanced/revanced-cli/issues/12)
2022-05-22 15:28:46 +00:00
oSumAtrIX
457c371fb9 Merge branch 'dev'
# Conflicts:
#	CHANGELOG.md
#	gradle.properties
2022-05-22 17:25:38 +02:00
oSumAtrIX
cd58fe5869 Merge pull request #16 from revanced/annotations
fix: support annotated patches
2022-05-22 17:15:56 +02:00
oSumAtrIX
f297f7d1ef add: signature checker and compatibility filters
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-22 17:10:43 +02:00
oSumAtrIX
51d250491f fix: breaking changes by revanced-patcher dependency
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-05-22 13:47:45 +02:00
semantic-release-bot
a7493e5e43 chore(release): 1.1.0 [skip ci]
# [1.1.0](https://github.com/revanced/revanced-cli/compare/v1.0.1...v1.1.0) (2022-05-07)

### Bug Fixes

* ClassLoader not working with Java 9+ ([3a11e11](3a11e1135b))
* leftover TODOs ([5b1139c](5b1139ce43))

### Features

* run `release.yml` workflow on branch `dev` ([9a64730](9a6473056b))
2022-05-07 19:26:36 +00:00
oSumAtrIX
46c275feb5 Merge pull request #10 from revanced/dev
feat: support java version 9+
2022-05-07 21:25:28 +02:00
11 changed files with 221 additions and 98 deletions

2
.idea/vcs.xml generated
View File

@@ -8,5 +8,7 @@
</component> </component>
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../revanced-patcher" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../revanced-patches" vcs="Git" />
</component> </component>
</project> </project>

View File

@@ -1,3 +1,33 @@
## [1.1.4](https://github.com/revanced/revanced-cli/compare/v1.1.3...v1.1.4) (2022-05-26)
### Bug Fixes
* migrate from `PatchLoader.load(...)` to `JarPatchBundle(...).loadPatches()` ([cabd32f](https://github.com/revanced/revanced-cli/commit/cabd32fda41d32616a61ae450c60e1ee7c35bc59))
## [1.1.3](https://github.com/revanced/revanced-cli/compare/v1.1.2...v1.1.3) (2022-05-25)
### Bug Fixes
* only accept directories when looking for files in resource patch ([c76da7e](https://github.com/revanced/revanced-cli/commit/c76da7e5ffa208860eea008dad358e4e3bb3d735))
## [1.1.2](https://github.com/revanced/revanced-cli/compare/v1.1.1...v1.1.2) (2022-05-22)
### Bug Fixes
* delete `outputFile` after deploying ([329f8a3](https://github.com/revanced/revanced-cli/commit/329f8a383fe52f4c2a66075d893c6599d3550bee))
## [1.1.1](https://github.com/revanced/revanced-cli/compare/v1.1.0...v1.1.1) (2022-05-22)
### Bug Fixes
* breaking changes by `revanced-patcher` dependency ([51d2504](https://github.com/revanced/revanced-cli/commit/51d250491f390695aedc64e7ee71a9dcf99d695c))
* wrong use of dependency to `revanced-patches` ([351de6c](https://github.com/revanced/revanced-cli/commit/351de6cb90aa0f2ec93e8b8f6c10d7d312082079))
* wrong use of variable substitution / typo ([81d53b5](https://github.com/revanced/revanced-cli/commit/81d53b5518454e479b7a8f2e9be934bee30702af)), closes [revanced/revanced-cli#12](https://github.com/revanced/revanced-cli/issues/12)
# [1.1.0-dev.3](https://github.com/revanced/revanced-cli/compare/v1.1.0-dev.2...v1.1.0-dev.3) (2022-05-15) # [1.1.0-dev.3](https://github.com/revanced/revanced-cli/compare/v1.1.0-dev.2...v1.1.0-dev.3) (2022-05-15)

View File

@@ -1,5 +1,5 @@
plugins { plugins {
kotlin("jvm") version "1.6.20" kotlin("jvm") version "1.6.21"
id("com.github.johnrengelman.shadow") version "7.1.2" id("com.github.johnrengelman.shadow") version "7.1.2"
java java
`maven-publish` `maven-publish`
@@ -23,13 +23,15 @@ repositories {
} }
dependencies { dependencies {
implementation(kotlin("stdlib")) implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
implementation("app.revanced:revanced-patcher:+") implementation("app.revanced:revanced-patcher:1.0.0-dev.16")
implementation("info.picocli:picocli:+") implementation("app.revanced:revanced-patches:1.0.0-dev.11")
implementation("info.picocli:picocli:4.6.3")
implementation("me.tongfei:progressbar:+")
implementation("com.github.li-wjohnson:jadb:master-SNAPSHOT") // using a fork instead. implementation("com.github.li-wjohnson:jadb:master-SNAPSHOT") // using a fork instead.
implementation("org.bouncycastle:bcpkix-jdk15on:+") implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21")
} }
java { java {

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 1.1.0-dev.3 version = 1.1.4

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,7 +1,11 @@
package app.revanced.cli package app.revanced.cli
import app.revanced.patch.Patches import app.revanced.patcher.annotation.Name
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.util.patch.implementation.JarPatchBundle
import app.revanced.utils.adb.Adb import app.revanced.utils.adb.Adb
import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.signature.Signature
import picocli.CommandLine.* import picocli.CommandLine.*
import java.io.File import java.io.File
@@ -16,7 +20,7 @@ internal object MainCommand : Runnable {
internal var includedPatches = arrayOf<String>() internal var includedPatches = arrayOf<String>()
@Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"]) @Option(names = ["-p", "--patches"], description = ["One or more bundles of patches"])
internal var patchBundles = arrayOf<File>() internal var patchBundles = arrayOf<String>()
@Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"], required = true) @Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"], required = true)
internal lateinit var cacheDirectory: String internal lateinit var cacheDirectory: String
@@ -33,6 +37,9 @@ internal object MainCommand : Runnable {
@Option(names = ["-l", "--list"], description = ["List patches only"]) @Option(names = ["-l", "--list"], description = ["List patches only"])
internal var listOnly: Boolean = false internal var listOnly: Boolean = false
@Option(names = ["-s", "--signature-checker"], description = ["Check signatures of all patches"])
internal var signatureCheck: Boolean = false
@Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"]) @Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"])
internal var mergeFiles = listOf<File>() internal var mergeFiles = listOf<File>()
@@ -47,36 +54,43 @@ internal object MainCommand : Runnable {
override fun run() { override fun run() {
if (listOnly) { if (listOnly) {
patchBundles.forEach { for (patchBundlePath in patchBundles)
Patches.load(it).forEach { for (it in JarPatchBundle(patchBundlePath).loadPatches())
println(it().metadata) println(
} "[available] ${
} it.javaClass.findAnnotationRecursively(
Name::class.java
)?.name ?: Name::class.java.name
}"
)
return return
} }
val patcher = app.revanced.patcher.Patcher( val patcher = app.revanced.patcher.Patcher(
inputFile, inputFile, cacheDirectory, patchResources
cacheDirectory,
patchResources
) )
Patcher.start(patcher) if (signatureCheck) {
patcher.addPatchesFiltered()
if (clean) { Signature.checkSignatures(patcher)
File(cacheDirectory).deleteRecursively() return
} }
val outputFile = File(outputPath) val outputFile = File(outputPath)
var adb: Adb? = null
deploy?.let { deploy?.let {
Adb( adb = Adb(
outputFile, outputFile, patcher.packageName, deploy!!
patcher.packageName, )
deploy!!
).deploy()
} }
Patcher.start(patcher)
if (clean) File(cacheDirectory).deleteRecursively()
adb?.deploy()
if (clean) outputFile.delete() if (clean) outputFile.delete()
} }
} }

View File

@@ -1,25 +1,22 @@
package app.revanced.cli package app.revanced.cli
import app.revanced.patch.Patches
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.patch.base.Patch
import app.revanced.utils.filesystem.FileSystemUtils import app.revanced.utils.filesystem.FileSystemUtils
import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.patcher.applyPatchesPrint
import app.revanced.utils.patcher.mergeFiles
import app.revanced.utils.signing.Signer import app.revanced.utils.signing.Signer
import java.io.File import java.io.File
import java.io.FileFilter
internal class Patcher { internal class Patcher {
internal companion object { internal companion object {
internal fun start(patcher: app.revanced.patcher.Patcher) { internal fun start(patcher: app.revanced.patcher.Patcher) {
// merge files like necessary integrations // merge files like necessary integrations
patcher.addFiles(MainCommand.mergeFiles) patcher.mergeFiles()
// add patches, but filter incompatible or excluded patches // add patches, but filter incompatible or excluded patches
patcher.addPatchesFiltered() patcher.addPatchesFiltered(includeFilter = MainCommand.includedPatches.isNotEmpty())
// apply patches // apply patches
for ((meta, result) in patcher.applyPatches { patcher.applyPatchesPrint()
println("Applying $it.")
}) {
println("Applied ${meta.name}. The result was $result.")
}
// write output file // write output file
val outFile = File(MainCommand.outputPath) val outFile = File(MainCommand.outputPath)
@@ -34,7 +31,7 @@ internal class Patcher {
} }
if (MainCommand.patchResources) { if (MainCommand.patchResources) {
for (file in File(MainCommand.cacheDirectory).resolve("build/").listFiles().first().listFiles()) { for (file in File(MainCommand.cacheDirectory).resolve("build/").listFiles(FileFilter { it.isDirectory })?.first()?.listFiles()!!) {
if (!file.isDirectory) { if (!file.isDirectory) {
zipFileSystem.replaceFile(file.name, file.readBytes()) zipFileSystem.replaceFile(file.name, file.readBytes())
continue continue
@@ -48,40 +45,10 @@ internal class Patcher {
// and sign the apk file // and sign the apk file
Signer.signApk(outFile) Signer.signApk(outFile)
println("[done]")
} }
private fun app.revanced.patcher.Patcher.addPatchesFiltered() {
val packageName = this.packageName
val packageVersion = this.packageVersion
val checkInclude = MainCommand.includedPatches.isNotEmpty()
MainCommand.patchBundles.forEach { bundle ->
val includedPatches = mutableListOf<Patch<Data>>()
Patches.load(bundle).forEach patch@{
val patch = it()
val filterOutPatches = true
if (filterOutPatches && !patch.metadata.compatiblePackages.any { packageMetadata ->
packageMetadata.name == packageName && packageMetadata.versions.any {
it == packageVersion
}
}) {
println("Skipping ${patch.metadata.name} due to incompatibility with current package $packageName.")
return@patch
}
if (checkInclude && !MainCommand.includedPatches.contains(patch.metadata.shortName)) {
return@patch
}
println("Adding ${patch.metadata.name}.")
includedPatches.add(patch)
} }
this.addPatches(includedPatches)
}
}
}
} }

View File

@@ -1,26 +0,0 @@
package app.revanced.patch
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.patch.base.Patch
import java.io.File
import java.net.URLClassLoader
internal object Patches {
/**
* This method loads patches from a given patch file
* @return the loaded patches represented as a list of functions returning instances of [Patch]
*/
internal fun load(patchesJar: File): List<() -> Patch<Data>> {
val url = patchesJar.toURI().toURL()
val classLoader = URLClassLoader(arrayOf(url))
val indexClass = classLoader.loadClass("app.revanced.patches.Index")
val index = indexClass.declaredFields.last()
index.isAccessible = true
@Suppress("UNCHECKED_CAST")
return index.get(null) as List<() -> Patch<Data>>
}
}

View File

@@ -8,7 +8,7 @@ internal object Constants {
private const val COMMAND_CHMOD_MOUNT = "chmod +x" private const val COMMAND_CHMOD_MOUNT = "chmod +x"
internal const val COMMAND_PID_OF = "pidof -s" internal const val COMMAND_PID_OF = "pidof -s"
internal const val COMMAND_CREATE_DIR = "mkdir -p" internal const val COMMAND_CREATE_DIR = "mkdir -p"
internal const val COMMAND_LOGCAT = "logcat -c && logcat --pid=$($COMMAND_PID_OF $PLACEHOLDER)" internal const val COMMAND_LOGCAT = "logcat -c && logcat | grep AndroidRuntime"
internal const val COMMAND_RESTART = "monkey -p $PLACEHOLDER 1 && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)" internal const val COMMAND_RESTART = "monkey -p $PLACEHOLDER 1 && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)"
// default mount file name // default mount file name

View File

@@ -0,0 +1,76 @@
package app.revanced.utils.patcher
import app.revanced.cli.MainCommand
import app.revanced.patcher.Patcher
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.base.Data
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.patch.base.Patch
import app.revanced.patcher.util.patch.implementation.JarPatchBundle
fun Patcher.addPatchesFiltered(
packageCompatibilityFilter: Boolean = true,
packageVersionCompatibilityFilter: Boolean = true,
includeFilter: Boolean = false
) {
val packageName = this.packageName
val packageVersion = this.packageVersion
MainCommand.patchBundles.forEach { bundle ->
val includedPatches = mutableListOf<Patch<Data>>()
JarPatchBundle(bundle).loadPatches().forEach patch@{ p ->
val patch = p.getDeclaredConstructor().newInstance()
val compatibilityAnnotation = patch.javaClass.findAnnotationRecursively(Compatibility::class.java)
val patchName = patch.javaClass.findAnnotationRecursively(Name::class.java)?.name ?: Name::class.java.name
val prefix = "[skipped] $patchName"
if (includeFilter && !MainCommand.includedPatches.contains(patchName)) {
println(prefix)
return@patch
}
if (packageVersionCompatibilityFilter || packageCompatibilityFilter) {
if (compatibilityAnnotation == null) {
println("$prefix: Missing compatibility annotation.")
return@patch
}
compatibilityAnnotation.compatiblePackages.forEach { compatiblePackage ->
if (packageCompatibilityFilter && compatiblePackage.name != packageName) {
println("$prefix: Package name not matching ${compatiblePackage.name}.")
return@patch
}
if (!packageVersionCompatibilityFilter || compatiblePackage.versions.any { it == packageVersion }) return@patch
println("$prefix: Unsupported version.")
return@patch
}
}
includedPatches.add(patch)
println("[added] $patchName")
}
this.addPatches(includedPatches)
}
}
fun Patcher.applyPatchesPrint() {
this.applyPatches().forEach { (patch, result) ->
if (result.isSuccess) {
println("[success] $patch")
return@forEach
}
println("[error] $patch:")
result.exceptionOrNull()!!.printStackTrace()
}
}
fun Patcher.mergeFiles() {
this.addFiles(MainCommand.mergeFiles)
}

View File

@@ -0,0 +1,58 @@
package app.revanced.utils.signature
import app.revanced.patcher.Patcher
import app.revanced.patcher.extensions.findAnnotationRecursively
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
import org.jf.dexlib2.iface.Method
object Signature {
fun checkSignatures(patcher: Patcher) {
val failed = mutableListOf<String>()
for (signature in patcher.resolveSignatures()) {
val signatureClass = signature::class.java
val signatureName =
signatureClass.findAnnotationRecursively(app.revanced.patcher.annotation.Name::class.java)?.name
?: signatureClass.name
if (!signature.resolved) {
failed.add(signatureName)
continue
}
val method = signature.result!!.method
val matchingMethod =
signatureClass.findAnnotationRecursively(MatchingMethod::class.java) ?: MatchingMethod()
println(
"""
[Signature] $signatureName
[Method] ${matchingMethod.definingClass}->${matchingMethod.name}
[Match] ${method.definingClass}->${method.toStr()}
""".trimIndent()
)
signatureClass.findAnnotationRecursively(FuzzyPatternScanMethod::class.java)?.let {
val warnings = signature.result!!.scanResult.warnings!!
println(
"""
[Warnings: ${warnings.count()}]
${warnings.joinToString(separator = "\n") { warning -> "${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})" }}
""".trimIndent()
)
}
}
println(
"""
${"=".repeat(50)}
[Failed signatures: ${failed.size}]
${failed.joinToString(separator = "\n") { it }}
""".trimIndent()
)
}
private fun Method.toStr(): String {
return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}"
}
}