mini miner writeup

This commit is contained in:
Yanbing Zhao
2020-11-06 23:26:15 +08:00
parent eb8a1d4acf
commit efdb90f0cf
3 changed files with 356 additions and 1 deletions
+1 -1
View File
@@ -39,7 +39,7 @@
| [证验码(未完成)](official/证验码/README.md) | [文件、源代码](official/证验码/src) |
| [动态链接库检查器(未完成)](official/动态链接库检查器/README.md) | [文件、源代码](official/动态链接库检查器/src) |
| [超精准的宇宙射线模拟器(未完成)](official/超精准的宇宙射线模拟器/README.md) | [文件、源代码](official/超精准的宇宙射线模拟器/src) |
| [超迷你的挖矿模拟器(未完成)](official/超迷你的挖矿模拟器/README.md) | [文件、源代码](official/超迷你的挖矿模拟器/src) |
| [超迷你的挖矿模拟器](official/超迷你的挖矿模拟器/README.md) | [文件、源代码](official/超迷你的挖矿模拟器/src) |
| [Flag 计算机](official/Flag%20计算机/README.md) | [文件、源代码](official/Flag%20计算机/src) |
| [中间人(未完成)](official/中间人/README.md) | [文件、源代码](official/中间人/src) |
| [不经意传输(未完成)](official/不经意传输/README.md) | [文件、源代码](official/不经意传输/src) |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,152 @@
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
public final class MiniMinerSolution {
public static final String ENCODED_TOKEN = "233333%3AMEUCIQDTTJu25fXbAgKcF%2BMDCGp1OSTd%2B4OwvNmTn%2FDhZryCiAIgDQXsmAHvLrAe2BSwaoSZO%2Bml6as%2BAj0N51PI3e6Befs";
public static final String DAMAGE_URL = "http://localhost:10169/api/damage?&x={0}&y={1}&material=DIAMOND&token=" + ENCODED_TOKEN;
public static final String STATE_URL = "http://localhost:10169/api/state?x=0&y=0&token=" + ENCODED_TOKEN;
public static final String RESET_URL = "http://localhost:10169/api/reset?token=" + ENCODED_TOKEN;
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
// reset
var body = HttpRequest.BodyPublishers.noBody();
var resetReq = HttpRequest.newBuilder(new URI(RESET_URL)).POST(body).build();
client.send(resetReq, HttpResponse.BodyHandlers.discarding());
System.out.println("Request: " + RESET_URL);
// get state
var stateReq = HttpRequest.newBuilder(new URI(STATE_URL)).GET().build();
var state = new JSONObject(client.send(stateReq, HttpResponse.BodyHandlers.ofString()).body());
System.out.println("Request: " + STATE_URL);
// handle state
var materials = new int[32][32];
for (var i = 0; i < 32; i++) {
for (var j = 0; j < 32; j++) {
switch (state.getJSONArray("materials").getJSONArray(i).getString(j)) {
case "FLAG":
materials[i][j] = 5;
break;
case "OBSIDIAN":
materials[i][j] = 4;
break;
case "DIAMOND":
materials[i][j] = 3;
break;
case "IRON":
materials[i][j] = 2;
break;
}
}
}
// collide lower seed
var rng = new Random();
var lowerSeeds = new ArrayList<Long>();
for (var lowerSeed = 0L; lowerSeed < 0x100000L; ++lowerSeed) {
if (testSeed(rng, lowerSeed, materials, 16, 16, 4) && testSeed(rng, lowerSeed, materials, 16, 32, 3)) {
lowerSeeds.add(lowerSeed);
}
}
var step = 0;
var total = lowerSeeds.size() * 256 + 1;
System.out.println("Collision: step " + ++step + " of " + total);
// collide the whole seed
var seeds = new ArrayList<Long>();
for (var lowerSeed : lowerSeeds) {
for (var mediumSeed = 0L; mediumSeed < 0x100L; ++mediumSeed) {
for (var upperSeed = 0L; upperSeed < 0x100000L; ++upperSeed) {
var seed = (upperSeed * 0x100L + mediumSeed) * 0x100000L + lowerSeed;
if (testSeed(rng, seed, materials, 48, 16, 4) && testSeed(rng, seed, materials, 48, 32, 3)) {
seeds.add(seed);
}
}
System.out.println("Collision: step " + ++step + " of " + total);
}
}
System.out.println("Collided seeds: " + seeds);
// damage possible flag locations
var nextSeeds = new ArrayList<Long>();
for (var seed : seeds) {
nextSeeds.add(seed * 8);
nextSeeds.add(seed * 8 + 1);
nextSeeds.add(seed * 8 + 2);
nextSeeds.add(seed * 8 + 3);
nextSeeds.add(seed * 8 + 4);
nextSeeds.add(seed * 8 + 5);
nextSeeds.add(seed * 8 + 6);
nextSeeds.add(seed * 8 + 7);
}
var futures = new ArrayList<CompletableFuture<?>>();
for (var nextSeed : nextSeeds) {
var chunkX = -1;
var x = 0x1800000;
var y = 0x1800000;
while (x >= 0x1000000 || y >= 0x1000000) {
rng.setSeed(nextSeed ^ (5 + 0x6E5D5AF15FA1280BL * ++chunkX));
rng.nextInt();
rng.nextInt();
x = Math.floorMod(rng.nextInt() + chunkX + 1, 0x1800000);
y = Math.floorMod(rng.nextInt() + 1, 0x1800000);
}
var damageUrlForSeed = MessageFormat.format(DAMAGE_URL, Integer.toString(x + 0x1000000 * chunkX), Integer.toString(y));
var damageReq = HttpRequest.newBuilder(new URI(damageUrlForSeed)).POST(body).build();
futures.add(client.sendAsync(damageReq, HttpResponse.BodyHandlers.ofString()).thenAccept(b -> {
var damaged = new JSONObject(b.body());
if ("FLAG".equals(damaged.getString("dropped"))) {
System.out.println("Flag: " + damaged.getString("flag"));
}
}));
System.out.println("Request: " + damageUrlForSeed);
}
// wait for 1 second and reset again
Thread.sleep(1000);
client.send(resetReq, HttpResponse.BodyHandlers.discarding());
System.out.println("Request: " + RESET_URL);
futures.forEach(CompletableFuture::join);
}
private static boolean testSeed(Random rng, long seed, int[][] materials, int modular, int count, int ordinal) {
var testMaterials = new boolean[modular][modular];
rng.setSeed(seed ^ ordinal);
for (var j = 0; j < count; ++j) {
var randomX = Math.floorMod(rng.nextInt() * ((1 << j) - 1) + 1, modular);
var randomY = Math.floorMod(rng.nextInt() * ((1 << j) - 1) + 1, modular);
testMaterials[randomX][randomY] = true;
}
for (var i = 0; i < 32; ++i) {
for (var j = 0; j < 32; ++j) {
if (materials[i][j] == ordinal && !testMaterials[i % modular][j % modular]) {
return false;
}
}
}
return true;
}
}