You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-03 03:43:05 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a123026f46 | ||
![]() |
9c39c9efdb | ||
![]() |
3ee1c01430 | ||
![]() |
64bae884dc | ||
![]() |
e94a706949 | ||
![]() |
89bb43066b | ||
![]() |
68174bbd6b |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
# [1.6.0](https://github.com/revanced/revanced-patcher/compare/v1.5.0...v1.6.0) (2022-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* use of `java.util.logging.Logger` ([9c39c9e](https://github.com/revanced/revanced-patcher/commit/9c39c9efdb5d48ddaffce7f711c275e732b0b2d9))
|
||||
|
||||
# [1.5.0](https://github.com/revanced/revanced-patcher/compare/v1.4.0...v1.5.0) (2022-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* use streams to write the dex files ([64bae88](https://github.com/revanced/revanced-patcher/commit/64bae884dcb72550a3218e149f3ca0fd0ca03aaf))
|
||||
|
||||
# [1.4.0](https://github.com/revanced/revanced-patcher/compare/v1.3.4...v1.4.0) (2022-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* return a `File` instance instead of `ExtFile` ([68174bb](https://github.com/revanced/revanced-patcher/commit/68174bbd6b4df47a91b610c2b97dbae55b594163))
|
||||
|
||||
## [1.3.4](https://github.com/revanced/revanced-patcher/compare/v1.3.3...v1.3.4) (2022-06-21)
|
||||
|
||||
|
||||
|
@@ -21,13 +21,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(kotlin("reflect"))
|
||||
|
||||
api("xpp3:xpp3:1.1.4c")
|
||||
api("app.revanced:multidexlib2:2.5.2.r2")
|
||||
api("org.smali:smali:2.5.2")
|
||||
api("org.apktool:apktool-lib:2.6.5-SNAPSHOT")
|
||||
implementation("xpp3:xpp3:1.1.4c")
|
||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||
implementation("org.smali:smali:2.5.2")
|
||||
implementation("org.apktool:apktool-lib:2.6.5-SNAPSHOT")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 1.3.4
|
||||
version = 1.6.0
|
||||
|
@@ -34,6 +34,7 @@ import org.jf.dexlib2.iface.DexFile
|
||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.logging.Logger
|
||||
|
||||
val NAMER = BasicDexFileNamer()
|
||||
|
||||
@@ -42,14 +43,20 @@ val NAMER = BasicDexFileNamer()
|
||||
* @param options The options for the patcher.
|
||||
*/
|
||||
class Patcher(private val options: PatcherOptions) {
|
||||
private val logger: Logger = Logger.getLogger(::Patcher.name)
|
||||
|
||||
val data: PatcherData
|
||||
private val opcodes: Opcodes
|
||||
|
||||
init {
|
||||
val extInputFile = ExtFile(options.inputFile)
|
||||
val outDir = File(options.resourceCacheDirectory)
|
||||
if (outDir.exists()) outDir.deleteRecursively()
|
||||
outDir.mkdirs()
|
||||
if (outDir.exists()){
|
||||
logger.info("Delete previous resource cache directory")
|
||||
|
||||
outDir.deleteRecursively()
|
||||
outDir.mkdirs()
|
||||
}
|
||||
|
||||
val androlib = Androlib(BuildOptions().also { it.setBuildOptions(options) })
|
||||
val resourceTable = androlib.getResTable(extInputFile, true)
|
||||
@@ -57,6 +64,8 @@ class Patcher(private val options: PatcherOptions) {
|
||||
val packageMetadata = PackageMetadata()
|
||||
|
||||
if (options.patchResources) {
|
||||
logger.info("Decode resources")
|
||||
|
||||
// decode resources to cache directory
|
||||
androlib.decodeManifestWithResources(extInputFile, outDir, resourceTable)
|
||||
androlib.decodeResourcesFull(extInputFile, outDir, resourceTable)
|
||||
@@ -71,6 +80,8 @@ class Patcher(private val options: PatcherOptions) {
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warning("Resource patching is disabled. Falling back to manually decode AndroidManifest.xml")
|
||||
|
||||
// create decoder for the resource table
|
||||
val decoder = ResAttrDecoder()
|
||||
decoder.currentPackage = ResPackage(resourceTable, 0, null)
|
||||
@@ -93,6 +104,8 @@ class Patcher(private val options: PatcherOptions) {
|
||||
packageMetadata.metaInfo.versionInfo = resourceTable.versionInfo
|
||||
packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo
|
||||
|
||||
logger.info("Read dex files")
|
||||
|
||||
// read dex files
|
||||
val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null)
|
||||
// get the opcodes
|
||||
@@ -115,17 +128,22 @@ class Patcher(private val options: PatcherOptions) {
|
||||
) {
|
||||
for (file in files) {
|
||||
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
|
||||
val e = data.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type }
|
||||
val type = classDef.type
|
||||
val e = data.bytecodeData.classes.internalClasses.findIndexed { it.type == type }
|
||||
if (e != null) {
|
||||
if (throwOnDuplicates) {
|
||||
throw Exception("Class ${classDef.type} has already been added to the patcher.")
|
||||
throw Exception("Class $type has already been added to the patcher.")
|
||||
}
|
||||
val (_, idx) = e
|
||||
if (allowedOverwrites.contains(classDef.type)) {
|
||||
if (allowedOverwrites.contains(type)) {
|
||||
logger.fine("Override $type")
|
||||
data.bytecodeData.classes.internalClasses[idx] = classDef
|
||||
continue
|
||||
}
|
||||
logger.fine("Skip $type")
|
||||
continue
|
||||
}
|
||||
logger.finest("Add $type")
|
||||
data.bytecodeData.classes.internalClasses.add(classDef)
|
||||
}
|
||||
}
|
||||
@@ -137,7 +155,7 @@ class Patcher(private val options: PatcherOptions) {
|
||||
fun save(): PatcherResult {
|
||||
val packageMetadata = data.packageMetadata
|
||||
val metaInfo = packageMetadata.metaInfo
|
||||
var resourceFile: ExtFile? = null
|
||||
var resourceFile: File? = null
|
||||
|
||||
if (options.patchResources) {
|
||||
val cacheDirectory = ExtFile(options.resourceCacheDirectory)
|
||||
@@ -173,12 +191,14 @@ class Patcher(private val options: PatcherOptions) {
|
||||
)
|
||||
}.toTypedArray()
|
||||
|
||||
logger.info("Building resources. This takes some time")
|
||||
|
||||
androlibResources.aaptPackage(
|
||||
aaptFile, manifestFile, resDirectory, null,
|
||||
null, includedFiles
|
||||
)
|
||||
|
||||
resourceFile = ExtFile(aaptFile)
|
||||
resourceFile = aaptFile
|
||||
}
|
||||
|
||||
val newDexFile = object : DexFile {
|
||||
@@ -192,6 +212,8 @@ class Patcher(private val options: PatcherOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Write dex files")
|
||||
|
||||
// write dex modified files
|
||||
val dexFiles = mutableMapOf<String, MemoryDataStore>()
|
||||
MultiDexIO.writeDexFile(
|
||||
@@ -201,7 +223,7 @@ class Patcher(private val options: PatcherOptions) {
|
||||
|
||||
return PatcherResult(
|
||||
dexFiles.map {
|
||||
app.revanced.patcher.util.dex.DexFile(it.key, it.value)
|
||||
app.revanced.patcher.util.dex.DexFile(it.key, it.value.readAt(0))
|
||||
},
|
||||
metaInfo.doNotCompress.toList(),
|
||||
resourceFile
|
||||
@@ -228,7 +250,11 @@ class Patcher(private val options: PatcherOptions) {
|
||||
val patchName = patch.patchName
|
||||
|
||||
// if the patch has already applied silently skip it
|
||||
if (appliedPatches.contains(patchName)) return PatchResultSuccess()
|
||||
if (appliedPatches.contains(patchName)){
|
||||
logger.fine("Skip $patchName because it already has been applied")
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
appliedPatches.add(patchName)
|
||||
|
||||
// recursively apply all dependency patches
|
||||
@@ -258,6 +284,8 @@ class Patcher(private val options: PatcherOptions) {
|
||||
data.bytecodeData
|
||||
}
|
||||
|
||||
logger.fine("Execute $patchName")
|
||||
|
||||
return try {
|
||||
patchInstance.execute(data)
|
||||
} catch (e: Exception) {
|
||||
|
@@ -2,15 +2,16 @@ package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.util.dex.DexFile
|
||||
import brut.directory.ExtFile
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* The result of a patcher.
|
||||
* @param dexFiles The patched dex files.
|
||||
* @param doNotCompress List of relative paths to files to exclude from compressing.
|
||||
* @param resourceFile ExtFile containing resources that need to be extracted into the APK.
|
||||
* @param resourceFile File containing resources that need to be extracted into the APK.
|
||||
*/
|
||||
data class PatcherResult(
|
||||
val dexFiles: List<DexFile>,
|
||||
val doNotCompress: List<String>? = null,
|
||||
val resourceFile: ExtFile?
|
||||
val resourceFile: File?
|
||||
)
|
@@ -3,6 +3,7 @@ package app.revanced.patcher.signature.implementation.method.resolver
|
||||
import app.revanced.patcher.data.PatcherData
|
||||
import app.revanced.patcher.data.implementation.proxy
|
||||
import app.revanced.patcher.extensions.MethodSignatureExtensions.fuzzyThreshold
|
||||
import app.revanced.patcher.extensions.MethodSignatureExtensions.name
|
||||
import app.revanced.patcher.extensions.parametersEqual
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import org.jf.dexlib2.Opcode
|
||||
@@ -11,37 +12,25 @@ import org.jf.dexlib2.iface.Method
|
||||
import org.jf.dexlib2.iface.instruction.Instruction
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import org.jf.dexlib2.iface.reference.StringReference
|
||||
import java.util.logging.Logger
|
||||
|
||||
internal class MethodSignatureResolver(
|
||||
private val classes: List<ClassDef>,
|
||||
private val methodSignatures: Iterable<MethodSignature>
|
||||
) {
|
||||
fun resolve(patcherData: PatcherData) {
|
||||
for (signature in methodSignatures) {
|
||||
for (classDef in classes) {
|
||||
for (method in classDef.methods) {
|
||||
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
|
||||
|
||||
// create class proxy, in case a patch needs mutability
|
||||
val classProxy = patcherData.bytecodeData.proxy(classDef)
|
||||
signature.result = SignatureResolverResult(
|
||||
classProxy,
|
||||
patternScanData,
|
||||
method,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These functions do not require the constructor values, so they can be static.
|
||||
companion object {
|
||||
private val LOGGER: Logger = Logger.getLogger(::MethodSignatureResolver.name)
|
||||
|
||||
fun resolveFromProxy(
|
||||
classProxy: app.revanced.patcher.util.proxy.ClassProxy,
|
||||
signature: MethodSignature
|
||||
): SignatureResolverResult? {
|
||||
for (method in classProxy.immutableClass.methods) {
|
||||
val result = compareSignatureToMethod(signature, method) ?: continue
|
||||
|
||||
LOGGER.fine("${signature.name} match to ${method.definingClass}->${method.name}")
|
||||
|
||||
return SignatureResolverResult(
|
||||
classProxy,
|
||||
result,
|
||||
@@ -156,6 +145,28 @@ internal class MethodSignatureResolver(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resolve(patcherData: PatcherData) {
|
||||
for (signature in methodSignatures) {
|
||||
val signatureName = signature.name
|
||||
LOGGER.fine("Resolve $signatureName")
|
||||
for (classDef in classes) {
|
||||
for (method in classDef.methods) {
|
||||
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
|
||||
|
||||
LOGGER.fine("$signatureName match to ${method.definingClass}->${method.name}")
|
||||
|
||||
// create class proxy, in case a patch needs mutability
|
||||
val classProxy = patcherData.bytecodeData.proxy(classDef)
|
||||
signature.result = SignatureResolverResult(
|
||||
classProxy,
|
||||
patternScanData,
|
||||
method,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun ClassDef.component1() = this
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package app.revanced.patcher.util.dex
|
||||
|
||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Wrapper for dex files.
|
||||
* @param name The original name of the dex file
|
||||
* @param memoryDataStore The data store for the dex file.
|
||||
* @param name The original name of the dex file.
|
||||
* @param dexFileInputStream The dex file as [InputStream].
|
||||
*/
|
||||
data class DexFile(val name: String, val memoryDataStore: MemoryDataStore)
|
||||
data class DexFile(val name: String, val dexFileInputStream: InputStream)
|
Reference in New Issue
Block a user