[+] Include Kotlin source code
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
.DS_Store
|
||||
.idea/
|
||||
*.iml
|
||||
.gradle/
|
||||
|
||||
+59
@@ -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) } }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'Hackergame2020'
|
||||
|
||||
@@ -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<String>)
|
||||
{
|
||||
// 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<Int>()
|
||||
var closestPrimePerm = listOf<Int>()
|
||||
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<Int>, count: Int): MutableList<Int>
|
||||
{
|
||||
val new = ArrayList<Int>(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<Int>
|
||||
{
|
||||
// It is a prime
|
||||
if (this <= 2) return listOf(this)
|
||||
|
||||
// Find prime numbers
|
||||
var temp = this
|
||||
val primes = ArrayList<Int>()
|
||||
for (i in 2 until temp)
|
||||
{
|
||||
while (temp % i == 0)
|
||||
{
|
||||
primes.add(i)
|
||||
temp /= i
|
||||
}
|
||||
}
|
||||
if(temp > 2) primes.add(temp)
|
||||
|
||||
return primes
|
||||
}
|
||||
|
||||
fun <T> List<T>.permute() = permuteHelper().distinct()
|
||||
|
||||
// https://code.sololearn.com/c24EP02YuQx3/#kt
|
||||
private fun <T> List<T>.permuteHelper(): List<List<T>>
|
||||
{
|
||||
if (size == 1) return listOf(this)
|
||||
|
||||
val perms = mutableListOf<List<T>>()
|
||||
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
|
||||
}
|
||||
+218
@@ -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<String>)
|
||||
{
|
||||
// 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<Account>,
|
||||
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
|
||||
}
|
||||
}
|
||||
+200
@@ -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<String>)
|
||||
{
|
||||
// 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 = """(?<=<p> \$).*(?=\$)""".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
|
||||
}
|
||||
+132
@@ -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<String>)
|
||||
{
|
||||
// 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<List<String>>,
|
||||
val min: List<Int>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DamageReturn(
|
||||
val dropped: String,
|
||||
val flag: String
|
||||
)
|
||||
|
||||
fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8")
|
||||
@@ -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<String>)
|
||||
{
|
||||
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)
|
||||
}
|
||||
+48
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user