Skip to content

Commit

Permalink
Fix the multiprocessing of the bruteforce as well as some missing bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
ruvmello committed Dec 17, 2023
1 parent d02e629 commit f5f2367
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 112 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fun main(args: Array<String>) {
"--help", "-h" -> {
println("This program aims to create a zip quine.")
println("The created zip contains the input file, as well as the zip itself.")
println("Usage: ./zipQuine inputFile [-o outputFile] [-h] [--debug]")
println("Usage: ./zipQuine inputFile [-o outputFile] [-h] [--debug] [--no-crc]")
exitProcess(0)
}
"--output", "-o" -> outputFilePath = arguments.removeAt(0)
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
package utils

fun findLastSublistOfByteArray(list: ByteArray, sublist: ByteArray): Int {
var index = 0
while (index < list.size) {
if (list[index] == sublist[0]) {
var isSublist = true
for (i in 1 until sublist.size) {
if (list[index + i] != sublist[i]) {
isSublist = false
index += i
break
}
}
if (isSublist) {
return index
}
} else {
index++
}
}
return -1
}

/**
* An integer has a size of 32 bits, get a UByteArray of the two least significant bytes
*
Expand Down
124 changes: 124 additions & 0 deletions src/main/kotlin/zip/CRC32Bruteforcer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package zip

import utils.findLastSublistOfByteArray
import utils.getByteArrayOf4Bytes
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.CountDownLatch

class CRC32Bruteforcer {
private val crc32Table = IntArray(256)

init {
this.calculateTable()
}

fun bruteforce(fullZipFile: ByteArray, quine: ByteArray, backupSize: Int, lhQuineSize: Int): ByteArray {
val range = Int.MIN_VALUE..Int.MAX_VALUE
val numThreads = Runtime.getRuntime().availableProcessors() // Adjust as needed
val segmentSize: Int = ((range.last.toLong() - range.first.toLong() + 1) / numThreads).toInt()

val firstPartLh = fullZipFile.copyOfRange(0, backupSize + 14)
val secondPartLh = fullZipFile.copyOfRange(backupSize + 18, backupSize + lhQuineSize + 5 + 14)

val indexOfFooterInQuine = findLastSublistOfByteArray(quine, byteArrayOf((80).toByte(), (75).toByte(), (1).toByte(), (2).toByte()))
val firstPartCd = fullZipFile.copyOfRange(backupSize + lhQuineSize + 5 + 18, backupSize + lhQuineSize + indexOfFooterInQuine + 16)
val secondPartCd = fullZipFile.copyOfRange(backupSize + lhQuineSize + indexOfFooterInQuine + 20, backupSize + lhQuineSize + quine.size + 16)
val lastPartCd = fullZipFile.copyOfRange(backupSize + lhQuineSize + quine.size + 20, fullZipFile.size)
val prevCalculatedCrc = calculateCRC32(firstPartLh)

val resultFound = AtomicBoolean(false)
val result = AtomicReference<ByteArray>()

val latch = CountDownLatch(numThreads)

for (i in 0 until numThreads) {
val start = range.first + i * segmentSize
val end = if (i == numThreads - 1) range.last else start + segmentSize - 1

val thread = Thread {
for (crc in start..end) {
if (resultFound.get()) {
break
}

val byteFormOfCrc = getByteArrayOf4Bytes(crc)
var currentCrcFile = byteFormOfCrc + secondPartLh + byteFormOfCrc + firstPartCd + byteFormOfCrc + secondPartCd + byteFormOfCrc + lastPartCd

if (calculateCRC32(currentCrcFile, prevCalculatedCrc) == crc) {
currentCrcFile = firstPartLh + currentCrcFile
result.set(currentCrcFile.clone())
resultFound.set(true)
break
}
}

latch.countDown()
}

thread.start()
}

try {
latch.await()
} catch (e: InterruptedException) {
e.printStackTrace()
}

return if (resultFound.get()) {
result.get()
} else {
assert(false) { "No CRC32 is found." }
byteArrayOf()
}
}

/**
* Calculate the CRC-32 table, so that it does not need to be recalculated when calling calculateCRC32()
*/
private fun calculateTable() {
// Populate the CRC32 lookup table
for (i in 0 until 256) {
var crc = i
for (j in 0 until 8) {
crc = if (crc and 1 == 1) {
(crc ushr 1) xor 0xEDB88320.toInt()
} else {
crc ushr 1
}
}
crc32Table[i] = crc
}
}

/**
* Calculate the CRC-32 checksum for a ByteArray
* More info: https://en.wikipedia.org/wiki/Cyclic_redundancy_check
*
* @param byteArray the ByteArray for which we calculate the CRC-32 checksum
* @return CRC-32 checksum
*/
fun calculateCRC32(byteArray: ByteArray): Int {
// Reference implementation that I ported to kotlin
// https://www.rosettacode.org/wiki/CRC-32#C
var crc32 = 0xFFFFFFFF.toInt()
for (byte in byteArray) {
val index = (crc32 and 0xFF) xor byte.toUByte().toInt()
crc32 = (crc32 ushr 8) xor crc32Table[index]
}

return crc32.inv() and 0xFFFFFFFF.toInt()
}

fun calculateCRC32(byteArray: ByteArray, prevCalculatedCrc: Int): Int {
// Reference implementation that I ported to kotlin
// https://www.rosettacode.org/wiki/CRC-32#C
var crc32 = prevCalculatedCrc
for (byte in byteArray) {
val index = (crc32 and 0xFF) xor byte.toUByte().toInt()
crc32 = (crc32 ushr 8) xor crc32Table[index]
}

return crc32.inv() and 0xFFFFFFFF.toInt()
}
}
35 changes: 0 additions & 35 deletions src/main/kotlin/zip/QuineGenerator.kt

This file was deleted.

Loading

0 comments on commit f5f2367

Please sign in to comment.