diff --git a/.gitignore b/.gitignore index 3a9a139..9f74e48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store .idea/ *.iml +.gradle/ diff --git a/players/Hykilpikonna/KotlinProject/build.gradle b/players/Hykilpikonna/KotlinProject/build.gradle new file mode 100755 index 0000000..b3b442f --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/build.gradle @@ -0,0 +1,59 @@ +plugins { + id 'java' + id 'org.jetbrains.kotlin.jvm' version '1.3.72' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.70' +} + +group 'org.hydev.experiment' +version '0.0.0.1' + +repositories { + mavenCentral() +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0" + + // https://mvnrepository.com/artifact/org.apache.commons/httpclient + compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.9' + + // https://mvnrepository.com/artifact/com.googlecode.aviator/aviator + compile group: 'com.googlecode.aviator', name: 'aviator', version: '5.1.4' + + // https://mvnrepository.com/artifact/com.google.zxing/core + compile group: 'com.google.zxing', name: 'core', version: '3.4.1' + + // https://mvnrepository.com/artifact/com.google.zxing/javase + compile group: 'com.google.zxing', name: 'javase', version: '3.4.1' + + // https://mvnrepository.com/artifact/com.beust/jcommander + compile group: 'com.beust', name: 'jcommander', version: '1.78' + + // https://mvnrepository.com/artifact/commons-io/commons-io + compile group: 'commons-io', name: 'commons-io', version: '2.8.0' + + // https://mvnrepository.com/artifact/com.google.guava/guava + compile group: 'com.google.guava', name: 'guava', version: '30.0-jre' + + // https://mvnrepository.com/artifact/org.hydev/HyLogger + compile group: 'org.hydev', name: 'HyLogger', version: '2.1.0.378' + + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +jar { + manifest { + attributes "Main-Class": "org.hydev.experiment.MainKt" + } + + from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } +} diff --git a/players/Hykilpikonna/KotlinProject/gradle.properties b/players/Hykilpikonna/KotlinProject/gradle.properties new file mode 100755 index 0000000..29e08e8 --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official \ No newline at end of file diff --git a/players/Hykilpikonna/KotlinProject/settings.gradle b/players/Hykilpikonna/KotlinProject/settings.gradle new file mode 100755 index 0000000..427b3db --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'Hackergame2020' + diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTF114514.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTF114514.kt new file mode 100644 index 0000000..3b3a273 --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTF114514.kt @@ -0,0 +1,289 @@ +package org.hydev.experiment + +import org.hydev.logger.HyLogger +import org.hydev.logger.HyLoggerConfig +import org.hydev.logger.appenders.FileAppender +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager +import kotlin.math.abs +import kotlin.math.roundToInt + +val symbols = "+-*/%^&| ".toCharArray() + +val scriptEngineManager = ScriptEngineManager() +val engine: ScriptEngine = scriptEngineManager.getEngineByName("AviatorScript") + +// Combination presets for the prime method +fun getCombs(vararg strs: String) = strs.map { str -> str.split(" ").map { it.toInt() } } +val comb1 = getCombs("114514") +val comb2 = getCombs("1 14514", "11 4514", "114 514", "1145 14", "11451 4") +val comb3 = getCombs("1 1 4514", "1 14 514", "1 145 14", "1 1451 4", "11 4 514", "11 45 14", "11 451 4", "114 5 14", "114 51 4", "1145 1 4") +val comb4 = getCombs("1 1 4 514", "1 1 45 14", "1 1 451 4", "1 14 5 14", "1 14 51 4", "11 4 5 14", "11 4 51 4", "11 45 1 4", "114 5 1 4") +val comb5 = getCombs("1 1 4 5 14", "1 1 4 51 4", "1 1 45 1 4", "1 14 5 1 4", "11 4 5 1 4") +val comb6 = getCombs("1 1 4 5 1 4") +val combs = listOf(getCombs(), comb1, comb2, comb3, comb4, comb5, comb6) + +val clipboard = ClipboardTools() + +/** + * TODO: Write a description for this class! + * + * @author HyDEV Team (https://github.com/HyDevelop) + * @author Hykilpikonna (https://github.com/hykilpikonna) + * @author Vanilla (https://github.com/VergeDX) + * @since 2020-11-03 21:44 + */ +fun main(args: Array) +{ + // HyLogger + HyLoggerConfig.appenders.add(FileAppender("./logs", "ctf-mini-miner.log")) + //HyLoggerConfig.installSysOut() + + // Actual program starts + val lastClip = clipboard.clipboardContents + while (true) + { + print("\n") + println("Waiting for clipboard...") + + // Wait for clipboard + while (clipboard.clipboardContents == lastClip || clipboard.getInt() == null) { Thread.sleep(200) } + println("Clipboard received!") + val target = clipboard.getInt()!! + findEquation(target) + } +} + +fun findEquation(target: Int) +{ + for (offsetIndex in 2..220) + { + val primeTotalOffset = if (offsetIndex % 2 == 0) offsetIndex / 2 else -offsetIndex / 2 + + var expr = findEquationHelper(target - primeTotalOffset) + + // Apply total offset + expr = if (primeTotalOffset >= 0) "${"-~".repeat(primeTotalOffset)}($expr)" + else "${"~-".repeat(-primeTotalOffset)}($expr)" + + // Check expression length + if (expr.length < 255) + { + println("Found! $expr") + + clipboard.clipboardContents = expr + + // Check correct + if (engine.eval(expr).toString().toInt() == target) println("Checked, correct!") + else error("Equation does not match :(") + + return + } + else continue + } + + error("Failed to find expression :(") + +// +// // If a short enough expression is not found, use the prime number method +// var originalPrimes = target.getPrimeFactors() +// var primeTotalOffset = 0 +// +// // Reduce prime targets to the least "prime" number around it (Eg. 3347 -> 3346) +// for (offset in -searchRange..searchRange) +// { +// if (target + offset < 0) continue +// +// val primes = (target - offset).getPrimeFactors() +// +// // New least "prime" number found! +// if (primes.size > originalPrimes.size) +// { +// originalPrimes = primes +// primeTotalOffset = offset +// } +// } +} + +fun findEquationHelper(target: Int): String +{ + val originalPrimes = target.getPrimeFactors() + + var closestPrimeComb = listOf() + var closestPrimePerm = listOf() + var closestPrimeOffset = Int.MAX_VALUE + + // Loop through all possible separations for combinations + for (n in 1..6) + { + // Loop through all combinations + for (combination in combs[n]) + { + // Loop through all permutations of prime numbers + for (primePerm in organizePrimes(originalPrimes, n).permute()) + { + if (primePerm.size != n || combination.size != n) + { + print("EERRRORRR") + } + + // Count the total offset + var offset = 0 + for (i in 0 until n) + { + offset += abs(primePerm[i] - combination[i]) + } + + // New closest found + if (offset < closestPrimeOffset) + { + closestPrimeComb = combination + closestPrimePerm = primePerm + closestPrimeOffset = offset + } + } + } + } + + // Found, construct expression + var expr = "" + for (i in closestPrimeComb.indices) + { + val value = closestPrimePerm[i] + val actual = closestPrimeComb[i] + + expr += when + { + actual == value -> actual + actual < value -> "${"~-".repeat(value - actual - 1)}~$actual" + else -> "${"~-".repeat(actual - value)}$actual" + } + + // Add multiplication symbol + if (i != closestPrimeComb.size - 1) expr += "*" + } + + // Fail + if (expr.length > 255) return expr + + // Evaluate expression and add neg sign if necessary + val eval = engine.eval(expr).toString().toInt() + if (eval < 0) expr = "-$expr" + return expr +} + +fun findEquationFallback(target: Int) +{ + // The symbol brute force method + var closest = "" + var closestValue = Int.MIN_VALUE + var closestOffset = Int.MAX_VALUE + + outer@for (one in symbols) + { + for (two in symbols) + { + for (three in symbols) + { + for (four in symbols) + { + for (five in symbols) + { + val text = "1${one}1${two}4${three}5${four}1${five}4".replace(" ", "") + val evalString = engine.eval(text).toString() + if (evalString.contains("E")) continue + val eval = evalString.toDouble().roundToInt() + + // New closest found + val offset = abs(eval - target) + if (offset < closestOffset) + { + closest = text + closestValue = eval + closestOffset = offset + + if (closestOffset < 50) break@outer + } + } + } + } + } + } + + HyLogger.general.timing.time().reset() + + // Create expression + val expr = if (closestValue <= target) "${"-~".repeat(target - closestValue)}($closest)" + else "-(${"-~".repeat(closestValue - target)}-($closest))" + + // Check expression length + if (expr.length < 255) println("Found! $expr") + else findEquation(target) +} + +/** + * Organize a list of primes so that it contains ${count} numbers + * + * @param primes + * @param count + * @return + */ +fun organizePrimes(primes: List, count: Int): MutableList +{ + val new = ArrayList(primes) + + // Eg. [2, 2, 2].size < 6 -> [1, 1, 1, 2, 2, 2] + if (primes.size < count) + { + for (i in 1..count - primes.size) new.add(0, 1) + return new + } + + // Eg. [2, 2, 2, 3, 3, 3, 5, 5, 5].size > 6 -> [2, 3, 3, 3, 4, 5, 5, 5] -> [3, 3, 4, 5, 5, 5, 6] -> [4, 5, 5, 5, 6, 9] + while (new.size > count) new.apply { add(removeAt(0) * removeAt(0)); sort() } + + return new +} + +fun Int.getPrimeFactors(): List +{ + // It is a prime + if (this <= 2) return listOf(this) + + // Find prime numbers + var temp = this + val primes = ArrayList() + for (i in 2 until temp) + { + while (temp % i == 0) + { + primes.add(i) + temp /= i + } + } + if(temp > 2) primes.add(temp) + + return primes +} + +fun List.permute() = permuteHelper().distinct() + +// https://code.sololearn.com/c24EP02YuQx3/#kt +private fun List.permuteHelper(): List> +{ + if (size == 1) return listOf(this) + + val perms = mutableListOf>() + val sub = this[0] + + for(perm in drop(1).permuteHelper()) + { + for (i in 0..perm.size) + { + val newPerm = perm.toMutableList() + newPerm.add(i, sub) + perms.add(newPerm) + } + } + return perms +} diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFDoggyBank.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFDoggyBank.kt new file mode 100644 index 0000000..bc552fa --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFDoggyBank.kt @@ -0,0 +1,218 @@ +package org.hydev.experiment + +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UnstableDefault +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.BasicCookieStore +import org.apache.http.impl.client.HttpClients +import org.apache.http.impl.cookie.BasicClientCookie +import org.apache.http.util.EntityUtils +import org.hydev.logger.HyLoggerConfig +import org.hydev.logger.appenders.FileAppender +import java.io.File +import kotlin.math.roundToInt +import kotlin.system.exitProcess + +/** + * TODO: Write a description for this class! + * + * @author HyDEV Team (https://github.com/HyDevelop) + * @author Hykilpikonna (https://github.com/hykilpikonna) + * @author Vanilla (https://github.com/VergeDX) + * @since 2020-11-02 15:51 + */ +@OptIn(UnstableDefault::class) +@ImplicitReflectionSerializer +fun main(args: Array) +{ + // Json + val json = Json(JsonConfiguration(isLenient = true)) + + // HyLogger + HyLoggerConfig.appenders.add(FileAppender("./logs", "ctf-doggy.log")) + HyLoggerConfig.installSysOut() + + // Create http client + val builder = HttpClients.custom() + val context = HttpClientContext.create() + val cookies = BasicCookieStore() + context.cookieStore = cookies + builder.setDefaultCookieStore(cookies) + val client = builder.build() + + // CTF session + val sessionFile = File("doggy-session.txt") + cookies.addCookie(BasicClientCookie("session", sessionFile.readText().replace("\n", "")) + .apply { domain = "202.38.93.111" }) + val auth = File("doggy-auth.txt").readText() + + val host = "http://202.38.93.111:10102/api/" + + // Function to access api + fun get(api: String, body: String): String + { + val get = if (body == "") HttpGet(host + api) + else HttpPost(host + api).apply { entity = StringEntity(body) } + + get.addHeader("authorization", auth) + get.addHeader("accept", "application/json, text/plain, */*") + get.addHeader("content-type", "application/json;charset=UTF-8") + val response = client.execute(get) + + if (response.statusLine.statusCode != 200) + { + println("Error $api - Body: $body ; Status code: ${response.statusLine.statusCode}") + } + + val responseBody = EntityUtils.toString(response.entity, "UTF-8") + response.close() + return responseBody + } + + fun getUser() = json.parse(User.serializer(), get("user", "")) + fun transfer(src: Int, dst: Int, amount: Int) + { + get("transfer", "{\"src\":$src,\"dst\":$dst,\"amount\":$amount}") + println("→ Transfer from $src to $dst, $amount") + } + + fun create(credit: Boolean) + { + get("create", "{\"type\":\"${if (credit) "credit" else "debit"}\"}") + println("+ Created ${if (credit) "credit" else "debit"} card.") + } + + fun eat(card: Int, log: Boolean = true) + { + get("eat", "{\"account\":$card}") + if (log) println("================ DAY ENDS ================") + } + + // Each problem + while (true) + { + // Backup session + val session = cookies.cookies.filter { it.name == "session" }[0].value + println("==============SESSION===============") + println(session) + println("================END=================") + sessionFile.writeText(session) + + // Get today + val user = getUser() + + // 后期可以不用四舍五入 bug 赚钱了 + if (user.printSummary()) + { + // 把所有储蓄卡转到卡 1 + user.debits().forEach { + if (it.balance != 0) transfer(it.id, 1, it.balance) + } + + // 把所有债还了 + user.credits().forEach { + if (it.balance != 0) transfer(1, it.id, it.balance) + } + + break + } + + // 初始化: 每 1 张信用卡添加 12 张储蓄卡 + if (user.date == 1 && user.credits().isEmpty()) + { + var idStart = 2 // 这一组的信用卡 ID + for (credit in 0..19) + { + create(true) + + for (debit in 1..12) + { + create(false) + + // 从信用卡给储蓄卡付款 167 + transfer(idStart, idStart + debit, 167) + } + idStart += 13 + } + } + + val debitThreshold = 10 + user.date / 10 + val creditThreshold = 100 + + // 检查每张储蓄卡都差不多 167, 多赚的转到卡 1 + user.debits().forEach { + if (it.balance > 167 + debitThreshold && it.id != 1) + { + Thread { transfer(it.id, 1, it.balance - 167) }.start() + Thread.sleep(100) + } + } + + // 让信用卡都小于 -2098,多欠的的用卡 1 付 + user.credits().forEach { + val transfer = it.balance - 2098 + creditThreshold + if (it.balance > 2098 && user.swap().balance > transfer) transfer(1, it.id, transfer) + } + + // 吃! + eat(1) + } + + // 后期模式, 开 15 个线程一起吃w + for (i in 0..14) + { + Thread { while (true) eat(1, false) }.start() + } + println("Rounding mode ended.") + + while (true) + { + when (readLine()!!.toLowerCase()) + { + "stop" -> exitProcess(0) + "summary" -> getUser().printSummary() + } + } +} + +@Serializable +data class Account( + val balance: Int, + val id: Int, + val type: String +) + +@Serializable +data class User( + val accounts: List, + val date: Int, + val flag: String? +) +{ + fun credits() = accounts.filter { it.type == "credit" } + fun debits() = accounts.filter { it.type == "debit" } + fun swap() = debits().find { it.id == 1 }!! + + /** + * Print summary and check if it's ready to leave the rounding profit system. + */ + fun printSummary(): Boolean + { + val dSum = debits().map { it.balance }.sum() + val cSum = credits().map { it.balance }.sum() + val dd = debits().map { (it.balance * 0.003).roundToInt() }.sum() + val dc = -credits().map { (it.balance * 0.005).roundToInt() }.sum() + val de = -10 + val sum = dd + dc + de + println("Summary - Total Balance: ${dSum - cSum} - DD: +$dd, DC: $dc, DE: $de, Sum: $sum") + + // Check if it's ready to leave the rounding profit system + return ((dSum - cSum) * 0.003).roundToInt() > 20 + } +} diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFIntegrals.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFIntegrals.kt new file mode 100644 index 0000000..b120a20 --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFIntegrals.kt @@ -0,0 +1,200 @@ +package org.hydev.experiment + +import org.apache.http.client.entity.UrlEncodedFormEntity +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.impl.client.BasicCookieStore +import org.apache.http.impl.client.HttpClients +import org.apache.http.impl.cookie.BasicClientCookie +import org.apache.http.message.BasicNameValuePair +import org.apache.http.util.EntityUtils +import org.hydev.logger.HyLoggerConfig +import org.hydev.logger.appenders.FileAppender +import java.awt.Robot +import java.awt.event.KeyEvent.* +import java.io.File + +/** + * TODO: Write a description for this class! + * + * @author HyDEV Team (https://github.com/HyDevelop) + * @author Hykilpikonna (https://github.com/hykilpikonna) + * @author Vanilla (https://github.com/VergeDX) + * @since 2020-11-02 15:51 + */ +fun main(args: Array) +{ + // HyLogger + HyLoggerConfig.appenders.add(FileAppender("./logs", "ctf.log")) + HyLoggerConfig.installSysOut() + + // Create http client + val builder = HttpClients.custom() + val context = HttpClientContext.create() + val cookies = BasicCookieStore() + context.cookieStore = cookies + builder.setDefaultCookieStore(cookies) + val client = builder.build() + + // Constants + val regex = """(?<=

\$).*(?=\$)""".toRegex() + val numberRegex = """(?<= )[0-9]*(?= 题)""".toRegex() + + // CTF session + val sessionFile = File("session") + cookies.addCookie(BasicClientCookie("session", sessionFile.readText().replace("\n", "")) + .apply { domain = "202.38.93.111" }) + + // Clipboard + val clipboard = ClipboardTools() + + // Robot: Keyboard control + val robot = Robot() + fun press(vararg events: Int) + { + events.forEach { robot.keyPress(it) } + robot.delay(100) + events.forEach { robot.keyRelease(it) } + } + + // Each problem + while (true) + { + // Backup session + val session = cookies.cookies.filter { it.name == "session" }[0].value + println("==============SESSION===============") + println(session) + println("================END=================") + sessionFile.writeText(session) + + // Get request + val get = HttpGet("http://202.38.93.111:10190/") + val response = client.execute(get) + val html = EntityUtils.toString(response.entity, "UTF-8") + val questionsRemaining = numberRegex.find(html)!!.value.toInt() + println("Questions remaining: $questionsRemaining") + val tex = regex.find(html)!!.value + response.close() + + // Do the last one on the website by yourself + if (questionsRemaining == 1) + { + println("===============FINISH================") + println("Now copy the session and override your browser cookie, " + + "then complete the last question in your browser to get the flag.") + return + } + + // Tex to function, copy + val eq = toEquation(tex) + println(eq) + clipboard.clipboardContents = eq + + // Robot: Auto paste and enter (META is the macOS command key. IF you're on Windows, change this to CONTROL) + press(VK_META, VK_V) + robot.delay(100) + press(VK_ENTER) + Thread.sleep(1000) + + // Robot: Auto copy result + press(VK_UP) + robot.delay(100) + press(VK_META, VK_C) // META: same here :) + robot.delay(100) + press(VK_DOWN) + + // Wait for clipboard + while (clipboard.getDouble() == null) { Thread.sleep(200) } + println("Clipboard received!") + val ans = clipboard.getDouble() + + // Send response + val post = HttpPost("http://202.38.93.111:10190/submit") + post.addHeader("content-type", "application/x-www-form-urlencoded") + post.entity = UrlEncodedFormEntity(listOf(BasicNameValuePair("ans", String.format("%.6f", ans))), "UTF-8") + val result = client.execute(post) + result.close() + } +} + +fun toEquation(originalTex: String): String +{ + val tex = originalTex + .replace("\\left(", "(").replace("\\right)", ")").replace("\\,", " ") + .replace("{", "(").replace("}", ")") + .replace("\\", "") + .shortenSpaces() + return toEquationHelper(tex) + .replace("x (", "x * (").replace("x(", "x * (") // Fix implied multiplication error +} + +fun toEquationHelper(originalTex: String): String +{ + var tex = originalTex + + if (tex.startsWith("int_(")) + { + tex = tex.substring(5, tex.length - 6) + val upper = tex.substring(0, tex.findBracket()) + tex = tex.substring(upper.length + 3) + val lower = tex.substring(0, tex.findBracket()) + tex = tex.substring(lower.length + 2) + return "integral(${toEquation(tex)},x,${toEquation(upper)},${toEquation(lower)})" + } + + // Change fractions + while (tex.contains("frac(")) + { + // frac(1)(2) -> (1)/(2) + val loc = tex.indexOf("frac(") + val endBrack = tex.findBracket(start = loc + 5) + tex = tex.substring(0, loc) + tex.substring(loc + 4, endBrack + 1) + "/" + tex.substring(endBrack + 1) + } + + return tex +} + +/** + * Find the index of the end bracket + * + * @param bracket { or ( + * @param start Start index + * @return + */ +fun String.findBracket(bracket: Boolean = false, start: Int = 0): Int +{ + val open = if (bracket) '{' else '(' + val close = if (bracket) '}' else ')' + + var level = 0 + var i = start + + while (i < length) + { + val c = this[i] + + if (c == close) + { + if (level == 0) return i + else level-- + } + if (c == open) level++; + + i++ + } + + return -1 +} + +/** + * Reduce double spaces to single spaces + * + * @return Shortened string + */ +fun String.shortenSpaces(): String +{ + var s = this + while (s.contains(" ")) s = s.replace(" ", " ") + return s +} diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFMiniMiner.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFMiniMiner.kt new file mode 100644 index 0000000..eff259b --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFMiniMiner.kt @@ -0,0 +1,132 @@ +package org.hydev.experiment + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.impl.client.BasicCookieStore +import org.apache.http.impl.client.HttpClients +import org.apache.http.util.EntityUtils +import org.hydev.logger.HyLoggerConfig +import org.hydev.logger.appenders.FileAppender +import java.io.File +import java.net.URLEncoder + + +/** + * TODO: Write a description for this class! + * + * @author HyDEV Team (https://github.com/HyDevelop) + * @author Hykilpikonna (https://github.com/hykilpikonna) + * @author Vanilla (https://github.com/VergeDX) + * @since 2020-11-03 16:19 + */ +fun main(args: Array) +{ + // Json + val json = Json(JsonConfiguration(isLenient = true)) + + // HyLogger + HyLoggerConfig.appenders.add(FileAppender("./logs", "ctf-mini-miner.log")) + HyLoggerConfig.installSysOut() + + // Create http client + val builder = HttpClients.custom() + val context = HttpClientContext.create() + val cookies = BasicCookieStore() + context.cookieStore = cookies + builder.setDefaultCookieStore(cookies) + val client = builder.build() + + // API stuff + val baseUrl = "http://localhost:8088/api/" + val token = File("mini-miner-token.txt").readText().replace("\n", "") + + fun post(api: String, params: String): String + { + // Create post + val url = "$baseUrl$api?token=${token.urlEncode()}$params" + val post = HttpPost(url).apply { + addHeader("accept", "*/*") + addHeader("content-type", "application/json;charset=UTF-8") + } + + // Execute + val response = client.execute(post) + val responseBody = EntityUtils.toString(response.entity, "UTF-8") + response.close() + + // Error + if (response.statusLine.statusCode != 200) + { + println("Error $api - Params: $params ; Status code: ${response.statusLine.statusCode} ; Response body: $responseBody") + } + + // Return body + return responseBody + } + fun state() = json.parse(StateReturn.serializer(), post("state", "&x=0&y=0")) + fun reset() = json.parse(ResetReturn.serializer(), post("reset", "")) + fun damage(x: Int, y: Int) = json.parse(DamageReturn.serializer(), post("damage", "&x=$x&y=$y&material=OBSIDIAN")) + + + // Actual code begins + + + while (true) + { + // Reset and get current state + println(reset()) + val state = state() + + // Find flag + var flagX = 0 + var flagY = 0 + for (x in 0..state.materials.size) + { + if (state.materials[x].contains("FLAG")) + { + flagX = x + flagY = state.materials[x].indexOf("FLAG") + break + } + } + + println("$flagX,$flagY") + + // Destroy block, wait for it to complete (long = 5s) + damage(flagX, flagY) + Thread.sleep(5500) + + // Destroy air, have roughly 3s until it checks the flag again + Thread { + val result = damage(flagX, flagY) + println(result) + }.start() + + Thread.sleep(500) + reset() // Reset the flag block + + } +} + +@Serializable +data class ResetReturn( + val expiration: String, + val user: String +) + +@Serializable +data class StateReturn( + val materials: List>, + val min: List +) + +@Serializable +data class DamageReturn( + val dropped: String, + val flag: String +) + +fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8") diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFQR.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFQR.kt new file mode 100644 index 0000000..ec955fb --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/CTFQR.kt @@ -0,0 +1,64 @@ +package org.hydev.experiment + +import com.google.zxing.BarcodeFormat.QR_CODE +import com.google.zxing.BinaryBitmap +import com.google.zxing.DecodeHintType.* +import com.google.zxing.client.j2se.BufferedImageLuminanceSource +import com.google.zxing.common.BitSource +import com.google.zxing.common.GlobalHistogramBinarizer +import com.google.zxing.multi.GenericMultipleBarcodeReader +import com.google.zxing.qrcode.QRCodeReader +import org.apache.commons.io.FileUtils +import java.io.File +import java.lang.System.err +import java.nio.charset.Charset +import javax.imageio.ImageIO + +// 这个是要改 ZXing 源码才会把解析的 byte[] 加进来啦w +var parsedBytes = byteArrayOf(); + +/** + * TODO: Write a description for this class! + * + * @author HyDEV Team (https://github.com/HyDevelop) + * @author Hykilpikonna (https://github.com/hykilpikonna) + * @author Vanilla (https://github.com/VergeDX) + * @since 2020-11-05 18:27 + */ +fun main(args: Array) +{ + var rawBytes = byteArrayOf() + var utfBytes = byteArrayOf() + var iso8859Bytes = byteArrayOf() + + File("/Users/hykilpikonna/Downloads/CTF/qr-frames").listFiles()!!.sorted().forEach { + + try + { + val source = BufferedImageLuminanceSource(ImageIO.read(it)) + val bitmap = BinaryBitmap(GlobalHistogramBinarizer(source)) + val reader = GenericMultipleBarcodeReader(QRCodeReader()) + val hints = mapOf(TRY_HARDER to true, POSSIBLE_FORMATS to QR_CODE, PURE_BARCODE to true) + + val result = reader.decodeMultiple(bitmap, hints)[0] + + rawBytes += result.rawBytes + utfBytes += result.text.toByteArray(Charset.forName("UTF-16")) + iso8859Bytes += result.text.toByteArray(Charset.forName("ISO-8859-1")) + } + catch (e: Exception) + { + err.println(it) + e.printStackTrace() + } + } + + val bits = BitSource(rawBytes) + val readBytes = ByteArray(rawBytes.size) + for (i in rawBytes.indices) + { + readBytes[i] = bits.readBits(8).toByte() + } + + FileUtils.writeByteArrayToFile(File("./qr_bytes_raw_processed"), parsedBytes) +} diff --git a/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/ClipboardTools.kt b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/ClipboardTools.kt new file mode 100644 index 0000000..221ef8e --- /dev/null +++ b/players/Hykilpikonna/KotlinProject/src/main/kotlin/org/hydev/experiment/ClipboardTools.kt @@ -0,0 +1,48 @@ +package org.hydev.experiment + +import java.awt.Toolkit +import java.awt.datatransfer.* + +/** + * http://www.javapractices.com/topic/TopicAction.do?Id=82 + */ +class ClipboardTools : ClipboardOwner +{ + /** + * Empty implementation of the ClipboardOwner interface. + */ + override fun lostOwnership(clipboard: Clipboard, contents: Transferable) {} + + fun getDouble() = clipboardContents!!.replace("−", "-").toDoubleOrNull(); + fun getInt() = clipboardContents!!.replace("−", "-").toIntOrNull(); + + /** + * Get and set the clipboard contents + */ + var clipboardContents: String? + get() + { + var result = "" + val clipboard = Toolkit.getDefaultToolkit().systemClipboard + val contents = clipboard.getContents(null) + val hasTransferableText = contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor) + if (hasTransferableText) + { + try + { + result = contents!!.getTransferData(DataFlavor.stringFlavor) as String + } + catch (ex: Exception) + { + ex.printStackTrace() + } + } + return result + } + set(string) + { + val stringSelection = StringSelection(string) + val clipboard = Toolkit.getDefaultToolkit().systemClipboard + clipboard.setContents(stringSelection, this) + } +} diff --git a/players/Hykilpikonna/README.md b/players/Hykilpikonna/README.md index cf59a78..c525c4b 100644 --- a/players/Hykilpikonna/README.md +++ b/players/Hykilpikonna/README.md @@ -15,6 +15,8 @@ ## 14: 来自未来的 ~~D-Mail~~ +[Kotlin 源码](./KotlinProject/src/main/kotlin/org/hydev/experiment/CTFQR.kt) + (会有 3020 的考古队看到这个嘛w?:thinking: 1. 把 QR **解析**成 `byte[]` 然后写到文件里(和 rawBytes 不一样哦! @@ -51,6 +53,8 @@ Python 的库试过的: zbar 装不上,qrtools 不能用来解析,pyzbar 很 ## 15. 狗狗银行 🐶🏦 +[Kotlin 源码](./KotlinProject/src/main/kotlin/org/hydev/experiment/CTFDoggyBank.kt) + ### 数学方面的 1. 信用卡欠 2099 的话每天的利息是 10.49(10),实际利率是 4.99% @@ -83,6 +87,8 @@ Python 的库试过的: zbar 装不上,qrtools 不能用来解析,pyzbar 很 ## 16. ∫(超基础的数理模拟器)dx +[Kotlin 源码](./KotlinProject/src/main/kotlin/org/hydev/experiment/CTFIntegrals.kt) + 虽然不是最高效的解但是视觉效果超厉害的!! **视频链接:** [ビリビリ](https://www.bilibili.com/video/BV16a411c7jN/) | [YouTube](https://youtu.be/8L2TkLmXngQ) @@ -107,6 +113,8 @@ Sage 看了下但是不知道怎样在程序里面控制w ## 19. 超恶臭的数字论证器 (确信) +[Kotlin 源码 (是最乱的一个 ;-;)](./KotlinProject/src/main/kotlin/org/hydev/experiment/CTF114514.kt) + 原来官方解法那么简单哇w 代码短 20 倍了!!!(╯‵□′)╯︵┻━┻ @@ -140,6 +148,8 @@ Sage 看了下但是不知道怎样在程序里面控制w ## 31. 超迷你的挖矿模拟器 ⛏️ +[Kotlin 源码](./KotlinProject/src/main/kotlin/org/hydev/experiment/CTFMiniMiner.kt) + 1. 挖掉 Flag 位置(1,1)的方块w(5s 2. 开始挖 Flag 位置的空气, 这个时候服务器检测到空气所以就执行可以掉落物品的代码w