Compare commits

..

24 Commits

Author SHA1 Message Date
semantic-release-bot
240ab18eaf chore(release): 2.9.9 [skip ci]
## [2.9.9](https://github.com/revanced/revanced-cli/compare/v2.9.8...v2.9.9) (2022-09-08)
2022-09-08 15:03:24 +00:00
Sculas
096dba2337 build: update patcher dependency 2022-09-08 17:02:02 +02:00
semantic-release-bot
26780f94e5 chore(release): 2.9.8 [skip ci]
## [2.9.8](https://github.com/revanced/revanced-cli/compare/v2.9.7...v2.9.8) (2022-09-08)

### Bug Fixes

* broken deprecation message ([e3e74ac](e3e74ac0e9))
2022-09-08 11:51:01 +00:00
Sculas
faa52e2c68 Merge remote-tracking branch 'origin/main' into main 2022-09-08 13:49:18 +02:00
Sculas
e3e74ac0e9 fix: broken deprecation message 2022-09-08 13:49:03 +02:00
Sculas
de1bdb708c build: update patcher 2022-09-08 13:46:23 +02:00
semantic-release-bot
9b2b933998 chore(release): 2.9.7 [skip ci]
## [2.9.7](https://github.com/revanced/revanced-cli/compare/v2.9.6...v2.9.7) (2022-09-08)
2022-09-08 10:55:29 +00:00
Sculas
fb5a72fdc0 build: remove useless stdlib 2022-09-08 12:53:38 +02:00
Sculas
86c5992630 build: update patcher 2022-09-08 12:53:08 +02:00
Sculas
06d2139ebf refactor: handle deprecation & cleanup 2022-09-08 12:52:00 +02:00
Sculas
15ba4f40cd chore: ignore kotlinc 2022-09-08 12:45:14 +02:00
semantic-release-bot
2d70037913 chore(release): 2.9.6 [skip ci]
## [2.9.6](https://github.com/revanced/revanced-cli/compare/v2.9.5...v2.9.6) (2022-09-07)
2022-09-07 21:17:46 +00:00
Canny
34108b9229 build: bump patcher dependency version (#126) 2022-09-07 23:16:12 +02:00
semantic-release-bot
64a323e7e7 chore(release): 2.9.5 [skip ci]
## [2.9.5](https://github.com/revanced/revanced-cli/compare/v2.9.4...v2.9.5) (2022-09-01)

### Bug Fixes

* mount bind revanced.apk from magisk's mirror ([372470c](372470c77b))
2022-09-01 00:41:46 +00:00
Nguyen Hoang The Vi
372470c77b fix: mount bind revanced.apk from magisk's mirror
Some banking apps detect the mountpoint of youtube base apk to determine device is rooted. So mount bind from magisk's mirror to trigger magisk unmount and let it hidden along with magisk hiding.
2022-09-01 02:40:20 +02:00
semantic-release-bot
3cef245728 chore(release): 2.9.4 [skip ci]
## [2.9.4](https://github.com/revanced/revanced-cli/compare/v2.9.3...v2.9.4) (2022-08-31)
2022-08-31 18:40:54 +00:00
oSumAtrIX
aa54a2b1a9 build: bump patcher dependency version
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-08-31 20:36:57 +02:00
semantic-release-bot
f8c430be33 chore(release): 2.9.3 [skip ci]
## [2.9.3](https://github.com/revanced/revanced-cli/compare/v2.9.2...v2.9.3) (2022-08-14)
2022-08-14 15:34:54 +00:00
oSumAtrIX
7692c18282 build: bump patcher dependency version 2022-08-14 17:33:14 +02:00
semantic-release-bot
469d0861af chore(release): 2.9.2 [skip ci]
## [2.9.2](https://github.com/revanced/revanced-cli/compare/v2.9.1...v2.9.2) (2022-08-07)
2022-08-07 20:42:09 +00:00
oSumAtrIX
208c5bc5b0 build: bump patcher dependency version 2022-08-07 21:55:15 +02:00
Ankit Majumder
82efd9941f refactor: spelling mistake (#114) 2022-08-06 09:54:37 +02:00
semantic-release-bot
48e9043517 chore(release): 2.9.1 [skip ci]
## [2.9.1](https://github.com/revanced/revanced-cli/compare/v2.9.0...v2.9.1) (2022-08-04)

### Reverts

* feat: remove extra zipalign step ([c3d8fec](c3d8fecad0))
2022-08-04 16:42:28 +00:00
Sculas
c3d8fecad0 revert: feat: remove extra zipalign step
This reverts commit c8e793efab.
2022-08-04 18:40:17 +02:00
14 changed files with 211 additions and 136 deletions

1
.idea/.gitignore generated vendored
View File

@@ -6,3 +6,4 @@
# Datasource local storage ignored files # Datasource local storage ignored files
/dataSources/ /dataSources/
/dataSources.local.xml /dataSources.local.xml
/kotlinc.xml

View File

@@ -1,3 +1,36 @@
## [2.9.9](https://github.com/revanced/revanced-cli/compare/v2.9.8...v2.9.9) (2022-09-08)
## [2.9.8](https://github.com/revanced/revanced-cli/compare/v2.9.7...v2.9.8) (2022-09-08)
### Bug Fixes
* broken deprecation message ([e3e74ac](https://github.com/revanced/revanced-cli/commit/e3e74ac0e9a844f9d717a499bca09e575dd90435))
## [2.9.7](https://github.com/revanced/revanced-cli/compare/v2.9.6...v2.9.7) (2022-09-08)
## [2.9.6](https://github.com/revanced/revanced-cli/compare/v2.9.5...v2.9.6) (2022-09-07)
## [2.9.5](https://github.com/revanced/revanced-cli/compare/v2.9.4...v2.9.5) (2022-09-01)
### Bug Fixes
* mount bind revanced.apk from magisk's mirror ([372470c](https://github.com/revanced/revanced-cli/commit/372470c77b82e8601ca523e87a2cfd44f79d0e31))
## [2.9.4](https://github.com/revanced/revanced-cli/compare/v2.9.3...v2.9.4) (2022-08-31)
## [2.9.3](https://github.com/revanced/revanced-cli/compare/v2.9.2...v2.9.3) (2022-08-14)
## [2.9.2](https://github.com/revanced/revanced-cli/compare/v2.9.1...v2.9.2) (2022-08-07)
## [2.9.1](https://github.com/revanced/revanced-cli/compare/v2.9.0...v2.9.1) (2022-08-04)
### Reverts
* feat: remove extra zipalign step ([c3d8fec](https://github.com/revanced/revanced-cli/commit/c3d8fecad0ed9d583b9f1f79bc271e0535d87be2))
# [2.9.0](https://github.com/revanced/revanced-cli/compare/v2.8.3...v2.9.0) (2022-08-03) # [2.9.0](https://github.com/revanced/revanced-cli/compare/v2.8.3...v2.9.0) (2022-08-03)

View File

@@ -23,10 +23,9 @@ repositories {
} }
dependencies { dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect")) implementation(kotlin("reflect"))
implementation("app.revanced:revanced-patcher:3.3.1") implementation("app.revanced:revanced-patcher:4.2.2")
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 = 2.9.0 version = 2.9.9

View File

@@ -0,0 +1,12 @@
package app.revanced.cli.aligning
import app.revanced.cli.command.MainCommand.logger
import app.revanced.utils.signing.align.ZipAligner
import java.io.File
object Aligning {
fun align(inputFile: File, outputFile: File) {
logger.info("Aligning ${inputFile.name} to ${outputFile.name}")
ZipAligner.align(inputFile, outputFile)
}
}

View File

@@ -1,5 +1,6 @@
package app.revanced.cli.command package app.revanced.cli.command
import app.revanced.cli.aligning.Aligning
import app.revanced.cli.logging.impl.DefaultCliLogger import app.revanced.cli.logging.impl.DefaultCliLogger
import app.revanced.cli.patcher.Patcher import app.revanced.cli.patcher.Patcher
import app.revanced.cli.patcher.logging.impl.PatcherLogger import app.revanced.cli.patcher.logging.impl.PatcherLogger
@@ -9,7 +10,7 @@ import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.description 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.impl.JarPatchBundle
import app.revanced.utils.adb.Adb import app.revanced.utils.adb.Adb
import picocli.CommandLine.* import picocli.CommandLine.*
import java.io.File import java.io.File
@@ -108,12 +109,12 @@ internal object MainCommand : Runnable {
@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"
@Option(names = ["-t", "--temp-dir"], description = ["Temporal resource cache directory"]) @Option(names = ["-t", "--temp-dir"], description = ["Temporary resource cache directory"])
var cacheDirectory = "revanced-cache" var cacheDirectory = "revanced-cache"
@Option( @Option(
names = ["-c", "--clean"], names = ["-c", "--clean"],
description = ["Clean the temporal resource cache directory. This will be done anyways when running the patcher"] description = ["Clean the temporary resource cache directory. This will be done anyways when running the patcher"]
) )
var clean: Boolean = false var clean: Boolean = false
@@ -160,11 +161,15 @@ internal object MainCommand : Runnable {
val cacheDirectory = File(pArgs.cacheDirectory) val cacheDirectory = File(pArgs.cacheDirectory)
// align the file
val alignedFile = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
Aligning.align(patchedFile, alignedFile)
// sign the file // sign the file
val finalFile = if (!pArgs.mount) { val finalFile = if (!pArgs.mount) {
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk") val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
Signing.sign( Signing.sign(
patchedFile, alignedFile,
signedOutput, signedOutput,
SigningOptions( SigningOptions(
pArgs.cn, pArgs.cn,
@@ -177,7 +182,7 @@ internal object MainCommand : Runnable {
signedOutput signedOutput
} else } else
patchedFile alignedFile
// finally copy to the specified output file // finally copy to the specified output file
logger.info("Copying ${finalFile.name} to ${outputFile.name}") logger.info("Copying ${finalFile.name} to ${outputFile.name}")

View File

@@ -2,12 +2,10 @@ package app.revanced.cli.patcher
import app.revanced.cli.command.MainCommand.args import app.revanced.cli.command.MainCommand.args
import app.revanced.cli.command.MainCommand.logger import app.revanced.cli.command.MainCommand.logger
import app.revanced.utils.filesystem.ZipFileSystemUtils
import app.revanced.utils.patcher.addPatchesFiltered import app.revanced.utils.patcher.addPatchesFiltered
import app.revanced.utils.patcher.applyPatchesVerbose import app.revanced.utils.patcher.applyPatchesVerbose
import app.revanced.utils.patcher.mergeFiles import app.revanced.utils.patcher.mergeFiles
import app.revanced.utils.signing.align.ZipAligner
import app.revanced.utils.signing.align.zip.ZipFile
import app.revanced.utils.signing.align.zip.structures.ZipEntry
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
@@ -25,22 +23,26 @@ internal object Patcher {
// write output file // write output file
if (output.exists()) Files.delete(output.toPath()) if (output.exists()) Files.delete(output.toPath())
inputFile.copyTo(output)
val result = patcher.save() val result = patcher.save()
ZipFile(output).use { outputFile -> ZipFileSystemUtils(output).use { outputFileSystem ->
// replace all dex files // replace all dex files
result.dexFiles.forEach { result.dexFiles.forEach {
logger.info("Writing dex file ${it.name}") logger.info("Writing dex file ${it.name}")
outputFile.addEntryCompressData(ZipEntry.createWithName(it.name), it.dexFileInputStream.readAllBytes()) outputFileSystem.write(it.name, it.stream.readAllBytes())
} }
if (!args.disableResourcePatching) { if (!args.disableResourcePatching) {
logger.info("Writing resources...") logger.info("Writing resources...")
outputFile.copyEntriesFromFileAligned(ZipFile(result.resourceFile!!), ZipAligner::getEntryAlignment) ZipFileSystemUtils(result.resourceFile!!).use { resourceFileSystem ->
val resourceFiles = resourceFileSystem.getFile(File.separator)
outputFileSystem.writePathRecursively(resourceFiles)
}
} }
outputFile.copyEntriesFromFileAligned(ZipFile(inputFile), ZipAligner::getEntryAlignment) result.doNotCompress?.let { outputFileSystem.uncompress(*it.toTypedArray()) }
} }
} }
} }

View File

@@ -44,12 +44,14 @@ internal object Constants {
internal val CONTENT_MOUNT_SCRIPT = internal val CONTENT_MOUNT_SCRIPT =
""" """
#!/system/bin/sh #!/system/bin/sh
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
base_path="$PATH_REVANCED_APP" base_path="$PATH_REVANCED_APP"
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' ) stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
chcon u:object_r:apk_data_file:s0 ${'$'}base_path chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}base_path ${'$'}stock_path mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
""".trimIndent() """.trimIndent()
} }

View File

@@ -0,0 +1,64 @@
package app.revanced.utils.filesystem
import java.io.Closeable
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.util.zip.ZipEntry
internal class ZipFileSystemUtils(
file: File
) : Closeable {
private var zipFileSystem = FileSystems.newFileSystem(file.toPath(), mapOf("noCompression" to true))
private fun Path.deleteRecursively() {
if (!Files.exists(this)) {
throw IllegalStateException("File exists in real folder but not in zip file system")
}
if (Files.isDirectory(this)) {
Files.list(this).forEach { path ->
path.deleteRecursively()
}
}
Files.delete(this)
}
internal fun getFile(path: String) = zipFileSystem.getPath(path)
internal fun writePathRecursively(path: Path) {
Files.list(path).use { fileStream ->
fileStream.forEach { filePath ->
val fileSystemPath = filePath.getRelativePath(path)
fileSystemPath.deleteRecursively()
}
}
Files.walk(path).use { fileStream ->
// don't include build directory
// by skipping the root node.
fileStream.skip(1).forEach { filePath ->
val relativePath = filePath.getRelativePath(path)
if (Files.isDirectory(filePath)) {
Files.createDirectory(relativePath)
return@forEach
}
Files.copy(filePath, relativePath)
}
}
}
internal fun write(path: String, content: ByteArray) = Files.write(zipFileSystem.getPath(path), content)
private fun Path.getRelativePath(path: Path): Path = zipFileSystem.getPath(path.relativize(this).toString())
// TODO: figure out why the file system is uncompressed by default and how to fix it
internal fun uncompress(vararg paths: String) =
paths.forEach { Files.setAttribute(zipFileSystem.getPath(it), "zip:method", ZipEntry.STORED) }
override fun close() = zipFileSystem.close()
}

View File

@@ -6,10 +6,11 @@ import app.revanced.cli.command.MainCommand.logger
import app.revanced.patcher.Patcher import app.revanced.patcher.Patcher
import app.revanced.patcher.data.Data import app.revanced.patcher.data.Data
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.deprecated
import app.revanced.patcher.extensions.PatchExtensions.include import app.revanced.patcher.extensions.PatchExtensions.include
import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.implementation.JarPatchBundle import app.revanced.patcher.util.patch.impl.JarPatchBundle
fun Patcher.addPatchesFiltered() { fun Patcher.addPatchesFiltered() {
val packageName = this.data.packageMetadata.packageName val packageName = this.data.packageMetadata.packageName
@@ -21,22 +22,28 @@ fun Patcher.addPatchesFiltered() {
val compatiblePackages = patch.compatiblePackages val compatiblePackages = patch.compatiblePackages
val patchName = patch.patchName val patchName = patch.patchName
val prefix = "Skipping $patchName" val prefix = "Skipping $patchName, reason"
val args = MainCommand.args.patchArgs?.patchingArgs!! val args = MainCommand.args.patchArgs?.patchingArgs!!
if (args.excludedPatches.contains(patchName)) { if (args.excludedPatches.contains(patchName)) {
logger.info("$prefix: Explicitly excluded") logger.info("$prefix: manually excluded")
return@patch return@patch
} else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) { } else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) {
logger.info("$prefix: Not explicitly included") logger.info("$prefix: excluded by default")
return@patch
}
patch.deprecated?.let { (reason, replacement) ->
logger.warn("$prefix: deprecated: $reason")
if (replacement != null) logger.warn("Either use ${replacement.java.patchName} instead or include it manually")
return@patch return@patch
} }
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.") if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
else { else {
if (!compatiblePackages.any { it.name == packageName }) { if (!compatiblePackages.any { it.name == packageName }) {
logger.warn("$prefix: Incompatible with $packageName. This patch is only compatible with ${ logger.warn("$prefix: incompatible with $packageName. This patch is only compatible with ${
compatiblePackages.joinToString( compatiblePackages.joinToString(
", " ", "
) { it.name } ) { it.name }
@@ -45,10 +52,10 @@ fun Patcher.addPatchesFiltered() {
} }
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) { if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
val compatibleWith = compatiblePackages.map { _package -> val compatibleWith = compatiblePackages.joinToString(";") { _package ->
"${_package.name}: ${_package.versions.joinToString(", ")}" "${_package.name}: ${_package.versions.joinToString(", ")}"
}.joinToString(";") }
logger.warn("$prefix: Incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith") logger.warn("$prefix: incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
return@patch return@patch
} }
} }

View File

@@ -1,11 +1,28 @@
package app.revanced.utils.signing.align package app.revanced.utils.signing.align
import app.revanced.utils.signing.align.zip.structures.ZipEntry import app.revanced.utils.signing.align.zip.ZipFile
import java.io.File
internal object ZipAligner { internal object ZipAligner {
private const val DEFAULT_ALIGNMENT = 4 private const val DEFAULT_ALIGNMENT = 4
private const val LIBRARY_ALIGNMENT = 4096 private const val LIBRARY_ALIGNMENT = 4096
fun getEntryAlignment(entry: ZipEntry): Int? = fun align(input: File, output: File) {
if (entry.compression.toUInt() != 0u) null else if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT val inputZip = ZipFile(input)
val outputZip = ZipFile(output)
for (entry in inputZip.entries) {
val data = inputZip.getDataForEntry(entry)
if (entry.compression == 0.toUShort()) {
val alignment = if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
outputZip.addEntryAligned(entry, data, alignment)
} else {
outputZip.addEntry(entry, data)
}
}
outputZip.finish()
}
} }

View File

@@ -2,21 +2,15 @@ package app.revanced.utils.signing.align.zip
import app.revanced.utils.signing.align.zip.structures.ZipEndRecord import app.revanced.utils.signing.align.zip.structures.ZipEndRecord
import app.revanced.utils.signing.align.zip.structures.ZipEntry import app.revanced.utils.signing.align.zip.structures.ZipEntry
import java.io.Closeable
import java.io.File import java.io.File
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.channels.FileChannel import java.nio.channels.FileChannel
import java.util.zip.CRC32
import java.util.zip.Deflater
class ZipFile(val file: File) : Closeable { class ZipFile(val file: File) {
var entries: MutableList<ZipEntry> = mutableListOf() var entries: MutableList<ZipEntry> = mutableListOf()
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw") private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
private var CDNeedsRewrite = false
private val compressionLevel = 5
init { init {
//if file isn't empty try to load entries //if file isn't empty try to load entries
@@ -59,24 +53,23 @@ class ZipFile(val file: File) : Closeable {
return buildList(numberOfEntries) { return buildList(numberOfEntries) {
for (i in 1..numberOfEntries) { for (i in 1..numberOfEntries) {
add( add(ZipEntry.fromCDE(filePointer).also
ZipEntry.fromCDE(filePointer).also {
{ //for some reason the local extra field can be different from the central one
//for some reason the local extra field can be different from the central one it.readLocalExtra(
it.readLocalExtra( filePointer.channel.map(
filePointer.channel.map( FileChannel.MapMode.READ_ONLY,
FileChannel.MapMode.READ_ONLY, it.localHeaderOffset.toLong() + 28,
it.localHeaderOffset.toLong() + 28, 2
2
)
) )
}) )
})
} }
} }
} }
private fun writeCD() { private fun writeCDE() {
val CDStart = filePointer.channel.position().toUInt() val CDEStart = filePointer.channel.position().toUInt()
entries.forEach { entries.forEach {
filePointer.channel.write(it.toCDE()) filePointer.channel.write(it.toCDE())
@@ -89,17 +82,15 @@ class ZipFile(val file: File) : Closeable {
0u, 0u,
entriesCount, entriesCount,
entriesCount, entriesCount,
filePointer.channel.position().toUInt() - CDStart, filePointer.channel.position().toUInt() - CDEStart,
CDStart, CDEStart,
"" ""
) )
filePointer.channel.write(endRecord.toECD()) filePointer.channel.write(endRecord.toECD())
} }
private fun addEntry(entry: ZipEntry, data: ByteBuffer) { fun addEntry(entry: ZipEntry, data: ByteBuffer) {
CDNeedsRewrite = true
entry.localHeaderOffset = filePointer.channel.position().toUInt() entry.localHeaderOffset = filePointer.channel.position().toUInt()
filePointer.channel.write(entry.toLFH()) filePointer.channel.write(entry.toLFH())
@@ -108,45 +99,17 @@ class ZipFile(val file: File) : Closeable {
entries.add(entry) entries.add(entry)
} }
fun addEntryCompressData(entry: ZipEntry, data: ByteArray) { fun addEntryAligned(entry: ZipEntry, data: ByteBuffer, alignment: Int) {
val compressor = Deflater(compressionLevel, true) //calculate where data would end up
compressor.setInput(data) val dataOffset = filePointer.filePointer + entry.LFHSize
compressor.finish()
val uncompressedSize = data.size val mod = dataOffset % alignment
val compressedData =
ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger
val compressedDataLength = compressor.deflate(compressedData) //wrong alignment
val compressedBuffer = if (mod != 0L) {
ByteBuffer.wrap(compressedData.take(compressedDataLength).toByteArray()) //add padding at end of extra field
entry.localExtraField =
compressor.end() entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
val crc = CRC32()
crc.update(data)
entry.compression = 8u //deflate compression
entry.uncompressedSize = uncompressedSize.toUInt()
entry.compressedSize = compressedDataLength.toUInt()
entry.crc32 = crc.value.toUInt()
addEntry(entry, compressedBuffer)
}
fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
alignment?.let { alignment ->
//calculate where data would end up
val dataOffset = filePointer.filePointer + entry.LFHSize
val mod = dataOffset % alignment
//wrong alignment
if (mod != 0L) {
//add padding at end of extra field
entry.localExtraField =
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
}
} }
addEntry(entry, data) addEntry(entry, data)
@@ -160,17 +123,8 @@ class ZipFile(val file: File) : Closeable {
) )
} }
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) { fun finish() {
for (entry in file.entries) { writeCDE()
if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates
val data = file.getDataForEntry(entry)
addEntryCopyData(entry, data, entryAlignment(entry))
}
}
override fun close() {
if (CDNeedsRewrite) writeCD()
filePointer.close() filePointer.close()
} }
} }

View File

@@ -58,8 +58,7 @@ data class ZipEndRecord(
fun toECD(): ByteBuffer { fun toECD(): ByteBuffer {
val commentBytes = fileComment.toByteArray(Charsets.UTF_8) val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size) val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size).also { it.order(ByteOrder.LITTLE_ENDIAN) }
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(ECD_SIGNATURE) buffer.putUInt(ECD_SIGNATURE)
buffer.putUShort(diskNumber) buffer.putUShort(diskNumber)

View File

@@ -1,6 +1,10 @@
package app.revanced.utils.signing.align.zip.structures package app.revanced.utils.signing.align.zip.structures
import app.revanced.utils.signing.align.zip.* import app.revanced.utils.signing.align.zip.getUShort
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 java.io.DataInput import java.io.DataInput
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
@@ -9,12 +13,12 @@ data class ZipEntry(
val version: UShort, val version: UShort,
val versionNeeded: UShort, val versionNeeded: UShort,
val flags: UShort, val flags: UShort,
var compression: UShort, val compression: UShort,
val modificationTime: UShort, val modificationTime: UShort,
val modificationDate: UShort, val modificationDate: UShort,
var crc32: UInt, val crc32: UInt,
var compressedSize: UInt, val compressedSize: UInt,
var uncompressedSize: UInt, val uncompressedSize: UInt,
val diskNumber: UShort, val diskNumber: UShort,
val internalAttributes: UShort, val internalAttributes: UShort,
val externalAttributes: UInt, val externalAttributes: UInt,
@@ -22,7 +26,7 @@ data class ZipEntry(
val fileName: String, val fileName: String,
val extraField: ByteArray, val extraField: ByteArray,
val fileComment: String, val fileComment: String,
var localExtraField: ByteArray = ByteArray(0), //separate for alignment var localExtraField: ByteArray = ByteArray(0), //seperate for alignment
) { ) {
val LFHSize: Int val LFHSize: Int
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
@@ -37,27 +41,6 @@ data class ZipEntry(
const val LFH_HEADER_SIZE = 30 const val LFH_HEADER_SIZE = 30
const val LFH_SIGNATURE = 0x04034b50u const val LFH_SIGNATURE = 0x04034b50u
fun createWithName(fileName: String): ZipEntry {
return ZipEntry(
0x1403u, //made by unix, version 20
0u,
0u,
0u,
0x0821u, //seems to be static time google uses, no idea
0x0221u, //same as above
0u,
0u,
0u,
0u,
0u,
0u,
0u,
fileName,
ByteArray(0),
""
)
}
fun fromCDE(input: DataInput): ZipEntry { fun fromCDE(input: DataInput): ZipEntry {
val signature = input.readUIntLE() val signature = input.readUIntLE()
@@ -84,8 +67,7 @@ data class ZipEntry(
val externalAttributes = input.readUIntLE() val externalAttributes = input.readUIntLE()
val localHeaderOffset = input.readUIntLE() val localHeaderOffset = input.readUIntLE()
val variableFieldsLength = val variableFieldsLength = fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
if (variableFieldsLength > 0) { if (variableFieldsLength > 0) {
val fileNameBytes = ByteArray(fileNameLength.toInt()) val fileNameBytes = ByteArray(fileNameLength.toInt())
@@ -99,8 +81,7 @@ data class ZipEntry(
fileComment = fileCommentBytes.toString(Charsets.UTF_8) fileComment = fileCommentBytes.toString(Charsets.UTF_8)
} }
flags = (flags and 0b1000u.inv() flags = (flags and 0b1000u.inv().toUShort()) //disable data descriptor flag as they are not used
.toUShort()) //disable data descriptor flag as they are not used
return ZipEntry( return ZipEntry(
version, version,
@@ -157,9 +138,8 @@ data class ZipEntry(
val nameBytes = fileName.toByteArray(Charsets.UTF_8) val nameBytes = fileName.toByteArray(Charsets.UTF_8)
val commentBytes = fileComment.toByteArray(Charsets.UTF_8) val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
val buffer = val buffer = ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size) .also { it.order(ByteOrder.LITTLE_ENDIAN) }
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
buffer.putUInt(CDE_SIGNATURE) buffer.putUInt(CDE_SIGNATURE)
buffer.putUShort(version) buffer.putUShort(version)